<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn get_usage() -> String {
    format!("{0} OPTION... COMMAND", executable!())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(u64),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(matches, options::INPUT)?,
            stdout: check_option(matches, options::OUTPUT)?,
            stderr: check_option(matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn parse_size(size: &str) -> Option<u64> {
    let ext = size.trim_start_matches(|c: char| c.is_digit(10));
    let num = size.trim_end_matches(char::is_alphabetic);
    let mut recovered = num.to_owned();
    recovered.push_str(ext);
    if recovered != size {
        return None;
    }
    let buf_size: u64 = match num.parse().ok() {
        Some(m) => m,
        None => return None,
    };
    let (power, base): (u32, u64) = match ext {
        "" => (0, 0),
        "KB" => (1, 1024),
        "K" => (1, 1000),
        "MB" => (2, 1024),
        "M" => (2, 1000),
        "GB" => (3, 1024),
        "G" => (3, 1000),
        "TB" => (4, 1024),
        "T" => (4, 1000),
        "PB" => (5, 1024),
        "P" => (5, 1000),
        "EB" => (6, 1024),
        "E" => (6, 1000),
        "ZB" => (7, 1024),
        "Z" => (7, 1000),
        "YB" => (8, 1024),
        "Y" => (8, 1000),
        _ => return None,
    };
    Some(buf_size * base.pow(power))
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => {
                let size = match parse_size(x) {
                    Some(m) => m,
                    None => return Err(ProgramOptionsError(format!("invalid mode {}", x))),
                };
                Ok(BufferType::Size(size))
            }
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
        .get_matches_from(args);

    let options = ProgramOptions::try_from(&matches)
        .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0));

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = match command.spawn() {
        Ok(p) => p,
        Err(e) => crash!(1, "failed to execute process: {}", e),
    };
    match process.wait() {
        Ok(status) => match status.code() {
            Some(i) => i,
            None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
        },
        Err(e) => crash!(1, "{}", e),
    }
}
<commit_msg>stdbuf: clean up returning Err<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.

// spell-checker:ignore (ToDO) tempdir dyld dylib dragonflybsd optgrps libstdbuf

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf;
use std::process::Command;
use tempfile::tempdir;
use tempfile::TempDir;
use uucore::InvalidEncodingHandling;

static ABOUT: &str =
    "Run COMMAND, with modified buffering operations for its standard streams.\n\n\
     Mandatory arguments to long options are mandatory for short options too.";
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
     This option is invalid with standard input.\n\n\
     If MODE is '0' the corresponding stream will be unbuffered.\n\n\
     Otherwise MODE is a number which may be followed by one of the following:\n\n\
     KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
     In this case the corresponding stream will be fully buffered with the buffer size set to \
     MODE bytes.\n\n\
     NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
     that will override corresponding settings changed by 'stdbuf'.\n\
     Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
     and are thus unaffected by 'stdbuf' settings.\n";

mod options {
    pub const INPUT: &str = "input";
    pub const INPUT_SHORT: &str = "i";
    pub const OUTPUT: &str = "output";
    pub const OUTPUT_SHORT: &str = "o";
    pub const ERROR: &str = "error";
    pub const ERROR_SHORT: &str = "e";
    pub const COMMAND: &str = "command";
}

fn get_usage() -> String {
    format!("{0} OPTION... COMMAND", executable!())
}

const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));

enum BufferType {
    Default,
    Line,
    Size(u64),
}

struct ProgramOptions {
    stdin: BufferType,
    stdout: BufferType,
    stderr: BufferType,
}

impl<'a> TryFrom<&ArgMatches<'a>> for ProgramOptions {
    type Error = ProgramOptionsError;

    fn try_from(matches: &ArgMatches) -> Result<Self, Self::Error> {
        Ok(ProgramOptions {
            stdin: check_option(matches, options::INPUT)?,
            stdout: check_option(matches, options::OUTPUT)?,
            stderr: check_option(matches, options::ERROR)?,
        })
    }
}

struct ProgramOptionsError(String);

#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd"
))]
fn preload_strings() -> (&'static str, &'static str) {
    ("LD_PRELOAD", "so")
}

#[cfg(target_vendor = "apple")]
fn preload_strings() -> (&'static str, &'static str) {
    ("DYLD_LIBRARY_PATH", "dylib")
}

#[cfg(not(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "dragonflybsd",
    target_vendor = "apple"
)))]
fn preload_strings() -> (&'static str, &'static str) {
    crash!(1, "Command not supported for this operating system!")
}

fn parse_size(size: &str) -> Option<u64> {
    let ext = size.trim_start_matches(|c: char| c.is_digit(10));
    let num = size.trim_end_matches(char::is_alphabetic);
    let mut recovered = num.to_owned();
    recovered.push_str(ext);
    if recovered != size {
        return None;
    }
    let buf_size: u64 = match num.parse().ok() {
        Some(m) => m,
        None => return None,
    };
    let (power, base): (u32, u64) = match ext {
        "" => (0, 0),
        "KB" => (1, 1024),
        "K" => (1, 1000),
        "MB" => (2, 1024),
        "M" => (2, 1000),
        "GB" => (3, 1024),
        "G" => (3, 1000),
        "TB" => (4, 1024),
        "T" => (4, 1000),
        "PB" => (5, 1024),
        "P" => (5, 1000),
        "EB" => (6, 1024),
        "E" => (6, 1000),
        "ZB" => (7, 1024),
        "Z" => (7, 1000),
        "YB" => (8, 1024),
        "Y" => (8, 1000),
        _ => return None,
    };
    Some(buf_size * base.pow(power))
}

fn check_option(matches: &ArgMatches, name: &str) -> Result<BufferType, ProgramOptionsError> {
    match matches.value_of(name) {
        Some(value) => match value {
            "L" => {
                if name == options::INPUT {
                    Err(ProgramOptionsError(
                        "line buffering stdin is meaningless".to_string(),
                    ))
                } else {
                    Ok(BufferType::Line)
                }
            }
            x => {
                let size = parse_size(x)
                    .ok_or_else(|| ProgramOptionsError(format!("invalid mode {}", x)))?;
                Ok(BufferType::Size(size))
            }
        },
        None => Ok(BufferType::Default),
    }
}

fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
    match buffer_type {
        BufferType::Size(m) => {
            command.env(buffer_name, m.to_string());
        }
        BufferType::Line => {
            command.env(buffer_name, "L");
        }
        BufferType::Default => {}
    }
}

fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
    let (preload, extension) = preload_strings();
    let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);

    let mut file = File::create(&inject_path)?;
    file.write_all(STDBUF_INJECT)?;

    Ok((preload.to_owned(), inject_path))
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();
    let usage = get_usage();

    let matches = App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .setting(AppSettings::TrailingVarArg)
        .arg(
            Arg::with_name(options::INPUT)
                .long(options::INPUT)
                .short(options::INPUT_SHORT)
                .help("adjust standard input stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::OUTPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::OUTPUT)
                .long(options::OUTPUT)
                .short(options::OUTPUT_SHORT)
                .help("adjust standard output stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::ERROR]),
        )
        .arg(
            Arg::with_name(options::ERROR)
                .long(options::ERROR)
                .short(options::ERROR_SHORT)
                .help("adjust standard error stream buffering")
                .value_name("MODE")
                .required_unless_one(&[options::INPUT, options::OUTPUT]),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .multiple(true)
                .takes_value(true)
                .hidden(true)
                .required(true),
        )
        .get_matches_from(args);

    let options = ProgramOptions::try_from(&matches)
        .unwrap_or_else(|e| crash!(125, "{}\nTry 'stdbuf --help' for more information.", e.0));

    let mut command_values = matches.values_of::<&str>(options::COMMAND).unwrap();
    let mut command = Command::new(command_values.next().unwrap());
    let command_params: Vec<&str> = command_values.collect();

    let mut tmp_dir = tempdir().unwrap();
    let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
    command.env(preload_env, libstdbuf);
    set_command_env(&mut command, "_STDBUF_I", options.stdin);
    set_command_env(&mut command, "_STDBUF_O", options.stdout);
    set_command_env(&mut command, "_STDBUF_E", options.stderr);
    command.args(command_params);

    let mut process = match command.spawn() {
        Ok(p) => p,
        Err(e) => crash!(1, "failed to execute process: {}", e),
    };
    match process.wait() {
        Ok(status) => match status.code() {
            Some(i) => i,
            None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
        },
        Err(e) => crash!(1, "{}", e),
    }
}
<|endoftext|>


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

use std::collections::HashSet;
use std::sync::Arc;

use common::ArgumentName;
use common::Diagnostic;
use common::DiagnosticsResult;
use common::NamedItem;
use common::WithLocation;
use graphql_ir::reexport::StringKey;
use graphql_ir::LinkedField;
use graphql_ir::OperationDefinition;
use graphql_ir::OperationDefinitionName;
use graphql_ir::Program;
use graphql_ir::ScalarField;
use graphql_ir::Selection;
use graphql_syntax::OperationKind;
use intern::string_key::Intern;
use lazy_static::lazy_static;
use relay_config::SchemaConfig;
use schema::Field;
use schema::FieldID;
use schema::ObjectID;
use schema::SDLSchema;
use schema::Schema;
use schema::Type;

use crate::get_normalization_operation_name;
use crate::match_::RawResponseGenerationMode;
use crate::relay_resolvers::get_bool_argument_is_true;
use crate::relay_resolvers::RELAY_RESOLVER_DIRECTIVE_NAME;
use crate::SplitOperationMetadata;
use crate::ValidationMessage;

lazy_static! {
    pub static ref HAS_OUTPUT_TYPE_ARGUMENT_NAME: ArgumentName =
        ArgumentName("has_output_type".intern());
}

fn generate_fat_selections_from_type(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    type_: Type,
    field_name: WithLocation<StringKey>,
) -> DiagnosticsResult<Vec<Selection>> {
    match type_ {
        Type::Object(object_id) => {
            let object = schema.object(object_id);
            if !object.is_extension {
                return Err(vec![Diagnostic::error(
                    ValidationMessage::RelayResolverServerTypeNotSupported {
                        field_name: field_name.item,
                        type_name: schema.get_type_name(type_),
                    },
                    field_name.location,
                )]);
            }

            let mut parent_types = HashSet::new();
            parent_types.insert(type_);
            generate_selections_from_object_fields(
                schema,
                schema_config,
                &object.fields,
                &mut parent_types,
            )
        }
        Type::Enum(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "enum".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Scalar(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "scalar".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::InputObject(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeInvalidInputObjectType {
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Interface(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "interface".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Union(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "union".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
    }
}

fn generate_selections_from_object_fields(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field_ids: &[FieldID],
    parent_types: &mut HashSet<Type>,
) -> DiagnosticsResult<Vec<Selection>> {
    let mut errors = vec![];
    let mut selections = vec![];
    for field_id in field_ids {
        if schema
            .field(*field_id)
            .directives
            .named(*RELAY_RESOLVER_DIRECTIVE_NAME)
            .is_some()
        {
            continue;
        }
        match generate_selection_from_field(schema, schema_config, field_id, parent_types) {
            Ok(selection) => selections.push(selection),
            Err(err) => {
                errors.extend(err);
            }
        }
    }
    if errors.is_empty() {
        Ok(selections)
    } else {
        Err(errors)
    }
}

fn generate_selection_from_field(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field_id: &FieldID,
    parent_types: &mut HashSet<Type>,
) -> DiagnosticsResult<Selection> {
    let field = schema.field(*field_id);
    let type_ = field.type_.inner();
    if field.name.item == schema_config.node_interface_id_field {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverIDFieldNotSupported {
                id_name: field.name.item,
            },
            field.name.location,
        )]);
    }

    if !field.arguments.is_empty() {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverArgumentsNotSupported,
            field.name.location,
        )]);
    }

    match type_ {
        Type::Enum(_) => Ok(Selection::ScalarField(Arc::new(ScalarField {
            alias: None,
            definition: WithLocation::generated(*field_id),
            arguments: vec![],
            directives: vec![],
        }))),
        Type::Scalar(_) => Ok(Selection::ScalarField(Arc::new(ScalarField {
            alias: None,
            definition: WithLocation::generated(*field_id),
            arguments: vec![],
            directives: vec![],
        }))),
        Type::Object(object_id) => {
            let selections = generate_selections_from_object(
                object_id,
                parent_types,
                schema,
                schema_config,
                field,
            )?;
            Ok(Selection::LinkedField(Arc::new(LinkedField {
                alias: None,
                definition: WithLocation::generated(*field_id),
                arguments: vec![],
                directives: vec![],
                selections,
            })))
        }
        Type::InputObject(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeInvalidInputObjectType {
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
        Type::Union(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "union".intern(),
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
        Type::Interface(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "interface".intern(),
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
    }
}

fn generate_selections_from_object(
    object_id: ObjectID,
    parent_types: &mut HashSet<Type>,
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field: &Field,
) -> Result<Vec<Selection>, Vec<Diagnostic>> {
    let object = schema.object(object_id);
    let type_ = Type::Object(object_id);

    if !object.is_extension {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverServerTypeNotSupported {
                field_name: field.name.item,
                type_name: object.name.item.0,
            },
            field.name.location,
        )]);
    }

    if parent_types.contains(&type_) {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverTypeRecursionDetected {
                type_name: object.name.item.0,
            },
            field.name.location,
        )]);
    }
    parent_types.insert(type_);
    let selections = generate_selections_from_object_fields(
        schema,
        schema_config,
        &object.fields,
        parent_types,
    )?;
    parent_types.remove(&type_);

    Ok(selections)
}

pub(crate) fn generate_name_for_nested_object_operation(
    schema: &SDLSchema,
    field: &Field,
) -> WithLocation<OperationDefinitionName> {
    let parent_type = field
        .parent_type
        .unwrap_or_else(|| panic!("Expected parent type for field {:?}.", field));

    let normalization_name = get_normalization_operation_name(
        format!("{}__{}", schema.get_type_name(parent_type), field.name.item).intern(),
    )
    .intern();

    field
        .name
        .map(|_| OperationDefinitionName(normalization_name))
}

pub fn generate_relay_resolvers_operations_for_nested_objects(
    program: &Program,
    schema_config: &SchemaConfig,
) -> DiagnosticsResult<Program> {
    let mut operations = vec![];
    let mut errors = vec![];
    for field in program.schema.get_fields() {
        if !field.is_extension {
            continue;
        }

        if let Some(directive) = field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME) {
            let has_output_type =
                get_bool_argument_is_true(&directive.arguments, *HAS_OUTPUT_TYPE_ARGUMENT_NAME);
            if !has_output_type {
                continue;
            }

            // Allow scalar/enums as @outputType
            if field.type_.inner().is_scalar() || field.type_.inner().is_enum() {
                continue;
            }

            let selections = match generate_fat_selections_from_type(
                &program.schema,
                schema_config,
                field.type_.inner(),
                field.name,
            ) {
                Ok(selections) => selections,
                Err(err) => {
                    errors.extend(err);
                    vec![]
                }
            };

            if !errors.is_empty() {
                continue;
            }

            let operation_name = generate_name_for_nested_object_operation(&program.schema, field);
            let directives = vec![
                SplitOperationMetadata {
                    location: field.name.location,
                    parent_documents: Default::default(),
                    derived_from: None,
                    raw_response_type_generation_mode: Some(
                        RawResponseGenerationMode::AllFieldsRequired,
                    ),
                }
                .to_directive(),
            ];

            let operation = OperationDefinition {
                name: operation_name,
                type_: field.type_.inner(),
                variable_definitions: vec![],
                directives,
                selections,
                kind: OperationKind::Query,
            };

            operations.push(Arc::new(operation))
        }
    }

    if errors.is_empty() {
        if operations.is_empty() {
            Ok(program.clone())
        } else {
            let mut next_program = program.clone();

            for operation in operations {
                next_program.insert_operation(operation)
            }

            Ok(next_program)
        }
    } else {
        Err(errors)
    }
}
<commit_msg>Use shared constant<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashSet;
use std::sync::Arc;

use common::Diagnostic;
use common::DiagnosticsResult;
use common::NamedItem;
use common::WithLocation;
use graphql_ir::reexport::StringKey;
use graphql_ir::LinkedField;
use graphql_ir::OperationDefinition;
use graphql_ir::OperationDefinitionName;
use graphql_ir::Program;
use graphql_ir::ScalarField;
use graphql_ir::Selection;
use graphql_syntax::OperationKind;
use intern::string_key::Intern;
use relay_config::SchemaConfig;
use schema::Field;
use schema::FieldID;
use schema::ObjectID;
use schema::SDLSchema;
use schema::Schema;
use schema::Type;

use crate::get_normalization_operation_name;
use crate::match_::RawResponseGenerationMode;
use crate::relay_resolvers::get_bool_argument_is_true;
use crate::relay_resolvers::RELAY_RESOLVER_DIRECTIVE_NAME;
use crate::relay_resolvers::RELAY_RESOLVER_HAS_OUTPUT_TYPE;
use crate::SplitOperationMetadata;
use crate::ValidationMessage;

fn generate_fat_selections_from_type(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    type_: Type,
    field_name: WithLocation<StringKey>,
) -> DiagnosticsResult<Vec<Selection>> {
    match type_ {
        Type::Object(object_id) => {
            let object = schema.object(object_id);
            if !object.is_extension {
                return Err(vec![Diagnostic::error(
                    ValidationMessage::RelayResolverServerTypeNotSupported {
                        field_name: field_name.item,
                        type_name: schema.get_type_name(type_),
                    },
                    field_name.location,
                )]);
            }

            let mut parent_types = HashSet::new();
            parent_types.insert(type_);
            generate_selections_from_object_fields(
                schema,
                schema_config,
                &object.fields,
                &mut parent_types,
            )
        }
        Type::Enum(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "enum".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Scalar(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "scalar".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::InputObject(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeInvalidInputObjectType {
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Interface(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "interface".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
        Type::Union(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "union".intern(),
                field_name: field_name.item,
                type_name: schema.get_type_name(type_),
            },
            field_name.location,
        )]),
    }
}

fn generate_selections_from_object_fields(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field_ids: &[FieldID],
    parent_types: &mut HashSet<Type>,
) -> DiagnosticsResult<Vec<Selection>> {
    let mut errors = vec![];
    let mut selections = vec![];
    for field_id in field_ids {
        if schema
            .field(*field_id)
            .directives
            .named(*RELAY_RESOLVER_DIRECTIVE_NAME)
            .is_some()
        {
            continue;
        }
        match generate_selection_from_field(schema, schema_config, field_id, parent_types) {
            Ok(selection) => selections.push(selection),
            Err(err) => {
                errors.extend(err);
            }
        }
    }
    if errors.is_empty() {
        Ok(selections)
    } else {
        Err(errors)
    }
}

fn generate_selection_from_field(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field_id: &FieldID,
    parent_types: &mut HashSet<Type>,
) -> DiagnosticsResult<Selection> {
    let field = schema.field(*field_id);
    let type_ = field.type_.inner();
    if field.name.item == schema_config.node_interface_id_field {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverIDFieldNotSupported {
                id_name: field.name.item,
            },
            field.name.location,
        )]);
    }

    if !field.arguments.is_empty() {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverArgumentsNotSupported,
            field.name.location,
        )]);
    }

    match type_ {
        Type::Enum(_) => Ok(Selection::ScalarField(Arc::new(ScalarField {
            alias: None,
            definition: WithLocation::generated(*field_id),
            arguments: vec![],
            directives: vec![],
        }))),
        Type::Scalar(_) => Ok(Selection::ScalarField(Arc::new(ScalarField {
            alias: None,
            definition: WithLocation::generated(*field_id),
            arguments: vec![],
            directives: vec![],
        }))),
        Type::Object(object_id) => {
            let selections = generate_selections_from_object(
                object_id,
                parent_types,
                schema,
                schema_config,
                field,
            )?;
            Ok(Selection::LinkedField(Arc::new(LinkedField {
                alias: None,
                definition: WithLocation::generated(*field_id),
                arguments: vec![],
                directives: vec![],
                selections,
            })))
        }
        Type::InputObject(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeInvalidInputObjectType {
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
        Type::Union(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "union".intern(),
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
        Type::Interface(_) => Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverOutputTypeUnsupported {
                type_kind: "interface".intern(),
                field_name: field.name.item,
                type_name: schema.get_type_name(type_),
            },
            field.name.location,
        )]),
    }
}

fn generate_selections_from_object(
    object_id: ObjectID,
    parent_types: &mut HashSet<Type>,
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    field: &Field,
) -> Result<Vec<Selection>, Vec<Diagnostic>> {
    let object = schema.object(object_id);
    let type_ = Type::Object(object_id);

    if !object.is_extension {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverServerTypeNotSupported {
                field_name: field.name.item,
                type_name: object.name.item.0,
            },
            field.name.location,
        )]);
    }

    if parent_types.contains(&type_) {
        return Err(vec![Diagnostic::error(
            ValidationMessage::RelayResolverTypeRecursionDetected {
                type_name: object.name.item.0,
            },
            field.name.location,
        )]);
    }
    parent_types.insert(type_);
    let selections = generate_selections_from_object_fields(
        schema,
        schema_config,
        &object.fields,
        parent_types,
    )?;
    parent_types.remove(&type_);

    Ok(selections)
}

pub(crate) fn generate_name_for_nested_object_operation(
    schema: &SDLSchema,
    field: &Field,
) -> WithLocation<OperationDefinitionName> {
    let parent_type = field
        .parent_type
        .unwrap_or_else(|| panic!("Expected parent type for field {:?}.", field));

    let normalization_name = get_normalization_operation_name(
        format!("{}__{}", schema.get_type_name(parent_type), field.name.item).intern(),
    )
    .intern();

    field
        .name
        .map(|_| OperationDefinitionName(normalization_name))
}

pub fn generate_relay_resolvers_operations_for_nested_objects(
    program: &Program,
    schema_config: &SchemaConfig,
) -> DiagnosticsResult<Program> {
    let mut operations = vec![];
    let mut errors = vec![];
    for field in program.schema.get_fields() {
        if !field.is_extension {
            continue;
        }

        if let Some(directive) = field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME) {
            let has_output_type =
                get_bool_argument_is_true(&directive.arguments, *RELAY_RESOLVER_HAS_OUTPUT_TYPE);
            if !has_output_type {
                continue;
            }

            // Allow scalar/enums as @outputType
            if field.type_.inner().is_scalar() || field.type_.inner().is_enum() {
                continue;
            }

            let selections = match generate_fat_selections_from_type(
                &program.schema,
                schema_config,
                field.type_.inner(),
                field.name,
            ) {
                Ok(selections) => selections,
                Err(err) => {
                    errors.extend(err);
                    vec![]
                }
            };

            if !errors.is_empty() {
                continue;
            }

            let operation_name = generate_name_for_nested_object_operation(&program.schema, field);
            let directives = vec![
                SplitOperationMetadata {
                    location: field.name.location,
                    parent_documents: Default::default(),
                    derived_from: None,
                    raw_response_type_generation_mode: Some(
                        RawResponseGenerationMode::AllFieldsRequired,
                    ),
                }
                .to_directive(),
            ];

            let operation = OperationDefinition {
                name: operation_name,
                type_: field.type_.inner(),
                variable_definitions: vec![],
                directives,
                selections,
                kind: OperationKind::Query,
            };

            operations.push(Arc::new(operation))
        }
    }

    if errors.is_empty() {
        if operations.is_empty() {
            Ok(program.clone())
        } else {
            let mut next_program = program.clone();

            for operation in operations {
                next_program.insert_operation(operation)
            }

            Ok(next_program)
        }
    } else {
        Err(errors)
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! `Transaction` related types.

use std::borrow::Cow;

use messages::Message;
use storage::{Fork, StorageValue};
use crypto::Hash;
use encoding::serialize::json::ExonumJson;

// TODO: Remove attribute when `const fn` becomes stable.
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
static MAX_RESERVED_VALUE: u16 = ::std::u8::MAX as u16;

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + ExonumJson + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// #
    /// use exonum::blockchain::Transaction;
    /// use exonum::crypto::PublicKey;
    /// use exonum::messages::Message;
    /// # use exonum::blockchain::TransactionStatus;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 32;
    ///         field public_key:    &PublicKey    [0 => 32]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     fn verify(&self) -> bool {
    ///         self.verify_signature(self.public_key())
    ///     }
    ///
    ///     // Other methods...
    ///     // ...
    /// #   fn execute(&self, _: &mut Fork) -> TransactionStatus { TransactionStatus::Succeeded }
    /// }
    /// # fn main() {}
    fn verify(&self) -> bool;

    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    /// - A transaction execution status (see `ExecutionStatus` for the details) is stored in the
    /// blockchain and can be accessed through api.
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// #
    /// use exonum::blockchain::{Transaction, TransactionStatus};
    /// use exonum::crypto::PublicKey;
    /// use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 32;
    /// #       field public_key:    &PublicKey    [0 => 32]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     fn execute(&self, fork: &mut Fork) -> TransactionStatus {
    ///         // Read and/or write into storage.
    ///         // ...
    ///
    ///         // Returns execution status.
    ///         TransactionStatus::Succeeded
    ///     }
    ///
    ///     // Other methods...
    ///     // ...
    /// #   fn verify(&self) -> bool { true }
    /// }
    /// # fn main() {}
    fn execute(&self, fork: &mut Fork) -> TransactionStatus;
}

/// Execution status of the transaction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TransactionStatus {
    /// Successful transaction execution.
    Succeeded,
    /// Panic occurred during transaction execution. This status should not be used explicitly.
    Panic,
    /// General failure (unspecified reason).
    Failed,
    /// User defined execution status. Can have different meanings for different transactions and
    /// services.
    Custom(u8),
}

impl<'a, T: Transaction> From<T> for Box<Transaction + 'a> {
    fn from(tx: T) -> Self {
        Box::new(tx) as Box<Transaction>
    }
}

impl StorageValue for TransactionStatus {
    fn hash(&self) -> Hash {
        u16::hash(&from_status(*self))
    }

    fn into_bytes(self) -> Vec<u8> {
        u16::into_bytes(from_status(self))
    }

    fn from_bytes(value: Cow<[u8]>) -> Self {
        to_status(u16::from_bytes(value))
    }
}

fn from_status(status: TransactionStatus) -> u16 {
    match status {
        TransactionStatus::Succeeded => 0,
        TransactionStatus::Panic => 1,
        TransactionStatus::Failed => 2,
        TransactionStatus::Custom(value) => u16::from(value) + MAX_RESERVED_VALUE + 1,
    }
}

fn to_status(value: u16) -> TransactionStatus {
    match value {
        0 => TransactionStatus::Succeeded,
        1 => TransactionStatus::Panic,
        2 => TransactionStatus::Failed,
        val => {
            assert!(
                val > MAX_RESERVED_VALUE && (val - MAX_RESERVED_VALUE - 1) <= u8::MAX_VALUE,
                "Invalid ExecutionStatus value: {}",
                val
            );
            TransactionStatus::Custom((val - MAX_RESERVED_VALUE - 1) as u8)
        }
    }
}

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

    #[test]
    fn execution_status_round_trip() {
        let statuses = [
            TransactionStatus::Succeeded,
            TransactionStatus::Panic,
            TransactionStatus::Failed,
            TransactionStatus::Custom(0),
            TransactionStatus::Custom(1),
            TransactionStatus::Custom(100),
            TransactionStatus::Custom(255),
        ];

        for status in &statuses {
            let bytes = status.clone().into_bytes();
            let new_status = TransactionStatus::from_bytes(Cow::Borrowed(&bytes));

            assert_eq!(*status, new_status);
        }
    }
}
<commit_msg>Use Result for Transaction::execute()<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! `Transaction` related types.

use std::borrow::Cow;

use messages::Message;
use storage::{Fork, StorageValue};
use crypto::Hash;
use encoding::serialize::json::ExonumJson;

// TODO: Remove attribute when `const fn` becomes stable.
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
static MAX_RESERVED_VALUE: u16 = ::std::u8::MAX as u16;

/// Result of the `Transaction`'s `execute' method.
pub type TransactionResult = Result<TransactionValue, TransactionError>;

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + ExonumJson + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// #
    /// use exonum::blockchain::Transaction;
    /// use exonum::crypto::PublicKey;
    /// use exonum::messages::Message;
    /// # use exonum::blockchain::TransactionStatus;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 32;
    ///         field public_key:    &PublicKey    [0 => 32]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     fn verify(&self) -> bool {
    ///         self.verify_signature(self.public_key())
    ///     }
    ///
    ///     // Other methods...
    ///     // ...
    /// #   fn execute(&self, _: &mut Fork) -> TransactionStatus { TransactionStatus::Succeeded }
    /// }
    /// # fn main() {}
    fn verify(&self) -> bool;

    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    /// - A transaction execution status (see `ExecutionStatus` for the details) is stored in the
    /// blockchain and can be accessed through api.
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// #
    /// use exonum::blockchain::{Transaction, TransactionStatus};
    /// use exonum::crypto::PublicKey;
    /// use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 32;
    /// #       field public_key:    &PublicKey    [0 => 32]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     fn execute(&self, fork: &mut Fork) -> TransactionStatus {
    ///         // Read and/or write into storage.
    ///         // ...
    ///
    ///         // Returns execution status.
    ///         TransactionStatus::Succeeded
    ///     }
    ///
    ///     // Other methods...
    ///     // ...
    /// #   fn verify(&self) -> bool { true }
    /// }
    /// # fn main() {}
    fn execute(&self, fork: &mut Fork) -> TransactionResult;
}

/// Result of successful transaction execution.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TransactionValue {
    /// Successful transaction execution.
    Success,
    /// User defined code. Can have different meanings for different transactions and services.
    Code(u8),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum TransactionError {
    /// Panic occurred during transaction execution. This status should not be used explicitly.
    Panic,
    /// General failure (unspecified reason).
    UnknownFailure,
    /// User defined error-code. Can have different meanings for different transactions and
    /// services.
    Code(u8)
}

/// Execution status of the transaction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TransactionStatus {
    /// Successful transaction execution.
    Succeeded,
    /// Panic occurred during transaction execution. This status should not be used explicitly.
    Panic,
    /// General failure (unspecified reason).
    Failed,
    /// User defined execution status. Can have different meanings for different transactions and
    /// services.
    Custom(u8),
}

impl<'a, T: Transaction> From<T> for Box<Transaction + 'a> {
    fn from(tx: T) -> Self {
        Box::new(tx) as Box<Transaction>
    }
}

impl StorageValue for TransactionStatus {
    fn hash(&self) -> Hash {
        u16::hash(&from_status(*self))
    }

    fn into_bytes(self) -> Vec<u8> {
        u16::into_bytes(from_status(self))
    }

    fn from_bytes(value: Cow<[u8]>) -> Self {
        to_status(u16::from_bytes(value))
    }
}

fn from_status(status: TransactionStatus) -> u16 {
    match status {
        TransactionStatus::Succeeded => 0,
        TransactionStatus::Panic => 1,
        TransactionStatus::Failed => 2,
        TransactionStatus::Custom(value) => u16::from(value) + MAX_RESERVED_VALUE + 1,
    }
}

fn to_status(value: u16) -> TransactionStatus {
    match value {
        0 => TransactionStatus::Succeeded,
        1 => TransactionStatus::Panic,
        2 => TransactionStatus::Failed,
        val => {
            assert!(
                val > MAX_RESERVED_VALUE && (val - MAX_RESERVED_VALUE - 1) <= u8::MAX_VALUE,
                "Invalid ExecutionStatus value: {}",
                val
            );
            TransactionStatus::Custom((val - MAX_RESERVED_VALUE - 1) as u8)
        }
    }
}

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

    #[test]
    fn execution_status_round_trip() {
        let statuses = [
            TransactionStatus::Succeeded,
            TransactionStatus::Panic,
            TransactionStatus::Failed,
            TransactionStatus::Custom(0),
            TransactionStatus::Custom(1),
            TransactionStatus::Custom(100),
            TransactionStatus::Custom(255),
        ];

        for status in &statuses {
            let bytes = status.clone().into_bytes();
            let new_status = TransactionStatus::from_bytes(Cow::Borrowed(&bytes));

            assert_eq!(*status, new_status);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Mikhail Vorotilov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use super::super::FloatType;
use super::super::Roots;

/// Solves a quartic equation a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0 = 0.
///
/// Returned roots are ordered.
/// Precision is about 5e-15 for f64, 5e-7 for f32.
///
/// # Examples
///
/// ```
/// use roots::find_roots_quartic;
///
/// let one_root = find_roots_quartic(1f64, 0f64, 0f64, 0f64, 0f64);
/// // Returns Roots::One([0f64]) as 'x^4 = 0' has one root 0
///
/// let two_roots = find_roots_quartic(1f32, 0f32, 0f32, 0f32, -1f32);
/// // Returns Roots::Two([-1f32, 1f32]) as 'x^4 - 1 = 0' has roots -1 and 1
/// ```
pub fn find_roots_quartic<F: FloatType>(a4: F, a3: F, a2: F, a1: F, a0: F) -> Roots<F> {
    // Handle non-standard cases
    if a4 == F::zero() {
        // a4 = 0; a3*x^3 + a2*x^2 + a1*x + a0 = 0; solve cubic equation
        super::cubic::find_roots_cubic(a3, a2, a1, a0)
    } else if a0 == F::zero() {
        // a0 = 0; x^4 + a2*x^2 + a1*x = 0; reduce to cubic and arrange results
        super::cubic::find_roots_cubic(a4, a3, a2, a1).add_new_root(F::zero())
    } else if a1 == F::zero() && a3 == F::zero() {
        // a1 = 0, a3 =0; a4*x^4 + a2*x^2 + a0 = 0; solve bi-quadratic equation
        super::biquadratic::find_roots_biquadratic(a4, a2, a0)
    } else {
        let _3 = F::three();
        let _4 = F::four();
        let _6 = F::four() + F::two();
        let _8 = F::four() + F::four();
        let _9 = _6 + _3;
        let _10 = _6 + _4;
        let _12 = _8 + _4;
        let _16 = _8 + _8;
        let _18 = _16 + F::two();
        let _27 = F::twenty_seven();
        let _64 = _8 * _8;
        let _72 = _64 + _8;
        let _80 = _64 + _16;
        let _128 = _64 + _64;
        let _144 = _80 + _64;
        let _192 = _128 + _64;
        let _256 = _192 + _64;
        // Discriminant
        // https://en.wikipedia.org/wiki/Quartic_function#Nature_of_the_roots
        let discriminant =
            _256 * a4 * a4 * a4 * a0 * a0 * a0
          - _192 * a4 * a4 * a3 * a1 * a0 * a0
          - _128 * a4 * a4 * a2 * a2 * a0 * a0
          + _144 * a4 * a4 * a2 * a1 * a1 * a0
          -  _27 * a4 * a4 * a1 * a1 * a1 * a1
          + _144 * a4 * a3 * a3 * a2 * a0 * a0
          -   _6 * a4 * a3 * a3 * a1 * a1 * a0
          -  _80 * a4 * a3 * a2 * a2 * a1 * a0
          +  _18 * a4 * a3 * a2 * a1 * a1 * a1
          +  _16 * a4 * a2 * a2 * a2 * a2 * a0
          -   _4 * a4 * a2 * a2 * a2 * a1 * a1
          -  _27 * a3 * a3 * a3 * a3 * a0 * a0
          +  _18 * a3 * a3 * a3 * a2 * a1 * a0
          -   _4 * a3 * a3 * a3 * a1 * a1 * a1
          -   _4 * a3 * a3 * a2 * a2 * a2 * a0
          +        a3 * a3 * a2 * a2 * a1 * a1
          ;
        let pp = _8 * a4 * a2 - _3 * a3 * a3;
        let rr = a3 * a3 * a3 + _8 * a4 * a4 * a1 - _4 * a4 * a3 * a2;
        let delta0 = a2 * a2 - _3 * a3 * a1 + _12 * a4 * a0;
        let dd =
             _64 * a4 * a4 * a4 * a0
           - _16 * a4 * a4 * a2 * a2
           + _16 * a4 * a3 * a3 * a2
           - _16 * a4 * a4 * a3 * a1
           -  _3 * a3 * a3 * a3 * a3;
        println!("quartic ∆:{:?},P:{:?},R:{:?},∆0:{:?},D:{:?}",discriminant,pp,rr,delta0,dd);

        // Handle special cases
        if discriminant == F::zero() && delta0 == F::zero() && dd == F::zero() {
            // Wiki: all four roots are equal
            Roots::One([-a3/(_4*a4)])
        }
        else if discriminant == F::zero() && delta0 == F::zero() {
            println!("quartic discriminant is zero, multiple roots path");
            // Wiki: At least three roots are equal to each other
            // x0 is the unique root of the remainder of the Euclidean division of the quartic by its second derivative
            //
            // Solved by SymPy (ra is the desired reminder)
            // a, b, c, d, e = symbols('a,b,c,d,e')
            // f=a*x**4+b*x**3+c*x**2+d*x+e     // Quartic polynom
            // g=6*a*x**2+3*b*x+c               // Second derivative
            // q, r = div(f, g)                 // SymPy only finds the highest power
            // simplify(f-(q*g+r)) == 0         // Verify the first division
            // qa, ra = div(r/a,g/a)            // Workaround to get the second division
            // simplify(f-((q+qa)*g+ra*a)) == 0 // Verify the second division
            // solve(ra,x)
            // ----- yields
            // (−72*a^2*e+10*a*c^2−3*b^2*c)/(9*(8*a^2*d−4*a*b*c+b^3))
            let x0 = (_72 * a4 * a4 * a0 + _10 * a4 * a2 * a2 - _3 * a1 * a1 * a2) / (_9 * (_8*a4*a4*a1-_4*a4*a3*a2+a3*a3*a3));
            let roots = Roots::One([x0]);
            roots.add_new_root(-(a3/a4+_3*x0))
        }
        else if discriminant == F::zero() && dd == F::zero() && pp > F::zero() && rr == F::zero() {
            // Wiki: two complex conjugate double roots
            println!("no real roots path");
            Roots::No([])
        }
        else {
            // Depressed quartic
            // https://en.wikipedia.org/wiki/Quartic_function#Converting_to_a_depressed_quartic

            // a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0 = 0 => x^4 + a*x^3 + b*x^2 + c*x + d = 0.
            let (a, b, c, d) = (a3 / a4, a2 / a4, a1 / a4, a0 / a4);
            // x^4 + a*x^3 + b*x^2 + c*x + d = 0 => y^4 + p*y^2 + q*y + r.
            let a_pow_2 = a * a;
            let a_pow_3 = a_pow_2 * a;
            let a_pow_4 = a_pow_2 * a_pow_2;
            let subst = -a3 / (F::four() * a4);
            let p = b - F::three() * a_pow_2 / _8;
            let q = a_pow_3 / _8 - a * b / F::two() + c;
            let r = d - F::three() * a_pow_4 / _256 - c * a / F::four() + a_pow_2 * b / _16;

            println!("Quartic depressed p:{:?},q:{:?},r:{:?}",p,q,r);
            let mut roots = Roots::No([]);
            for x in super::quartic_depressed::find_roots_quartic_depressed(p, q, r)
                .as_ref()
                .iter()
            {
                println!("Quartic depressed root {:?}",*x);
                roots = roots.add_new_root(*x + subst);
            }
            roots
        }
    }
}

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

    #[test]
    fn test_find_roots_quartic() {
        assert_eq!(find_roots_quartic(1f32, 0f32, 0f32, 0f32, 0f32), Roots::One([0f32]));
        assert_eq!(find_roots_quartic(1f64, 0f64, 0f64, 0f64, -1f64), Roots::Two([-1f64, 1f64]));
        assert_eq!(
            find_roots_quartic(1f64, -10f64, 35f64, -50f64, 24f64),
            Roots::Four([1f64, 2f64, 3f64, 4f64])
        );

        match find_roots_quartic(3f64, 5f64, -5f64, -5f64, 2f64) {
            Roots::Four(x) => {
                assert_float_array_eq!(2e-15f64, x, [-2f64, -1f64, 0.33333333333333333f64, 1f64]);
            }
            _ => {
                assert!(false);
            }
        }

        match find_roots_quartic(3f32, 5f32, -5f32, -5f32, 2f32) {
            Roots::Four(x) => {
                assert_float_array_eq!(5e-7, x, [-2f32, -1f32, 0.33333333333333333f32, 1f32]);
            }
            _ => {
                assert!(false);
            }
        }
    }

    #[test]
    fn test_find_roots_quartic_tim_luecke() {
        assert_eq!(find_roots_quartic(-14.0625f64, -3.75f64, 29.75f64, 4.0f64, -16.0f64), Roots::Two([-1.1016117f64,0.96827835f64]));
        assert_eq!(find_roots_quartic(-14.0625f32, -3.75f32, 29.75f32, 4.0f32, -16.0f32), Roots::Two([-1.1016117f32,0.96827835f32]));
    }
}
<commit_msg>triple and quadruple roots of quartic equation<commit_after>// Copyright (c) 2015, Mikhail Vorotilov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use super::super::FloatType;
use super::super::Roots;

/// Solves a quartic equation a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0 = 0.
///
/// Returned roots are ordered.
/// Precision is about 5e-15 for f64, 5e-7 for f32.
///
/// # Examples
///
/// ```
/// use roots::find_roots_quartic;
///
/// let one_root = find_roots_quartic(1f64, 0f64, 0f64, 0f64, 0f64);
/// // Returns Roots::One([0f64]) as 'x^4 = 0' has one root 0
///
/// let two_roots = find_roots_quartic(1f32, 0f32, 0f32, 0f32, -1f32);
/// // Returns Roots::Two([-1f32, 1f32]) as 'x^4 - 1 = 0' has roots -1 and 1
/// ```
pub fn find_roots_quartic<F: FloatType>(a4: F, a3: F, a2: F, a1: F, a0: F) -> Roots<F> {
    // Handle non-standard cases
    if a4 == F::zero() {
        // a4 = 0; a3*x^3 + a2*x^2 + a1*x + a0 = 0; solve cubic equation
        super::cubic::find_roots_cubic(a3, a2, a1, a0)
    } else if a0 == F::zero() {
        // a0 = 0; x^4 + a2*x^2 + a1*x = 0; reduce to cubic and arrange results
        super::cubic::find_roots_cubic(a4, a3, a2, a1).add_new_root(F::zero())
    } else if a1 == F::zero() && a3 == F::zero() {
        // a1 = 0, a3 =0; a4*x^4 + a2*x^2 + a0 = 0; solve bi-quadratic equation
        super::biquadratic::find_roots_biquadratic(a4, a2, a0)
    } else {
        let _3 = F::three();
        let _4 = F::four();
        let _6 = F::four() + F::two();
        let _8 = F::four() + F::four();
        let _9 = _6 + _3;
        let _10 = _6 + _4;
        let _12 = _8 + _4;
        let _16 = _8 + _8;
        let _18 = _16 + F::two();
        let _27 = F::twenty_seven();
        let _64 = _8 * _8;
        let _72 = _64 + _8;
        let _80 = _64 + _16;
        let _128 = _64 + _64;
        let _144 = _80 + _64;
        let _192 = _128 + _64;
        let _256 = _192 + _64;
        // Discriminant
        // https://en.wikipedia.org/wiki/Quartic_function#Nature_of_the_roots
        let discriminant = _256 * dbg!(a4) * a4 * a4 * a0 * a0 * a0
            - _192 * a4 * a4 * dbg!(a3) * a1 * a0 * a0
            - _128 * a4 * a4 * dbg!(a2) * a2 * a0 * a0
            + _144 * a4 * a4 * a2 * dbg!(a1) * a1 * a0
            - _27 * a4 * a4 * a1 * a1 * a1 * a1
            + _144 * a4 * a3 * a3 * a2 * dbg!(a0) * a0
            - _6 * a4 * a3 * a3 * a1 * a1 * a0
            - _80 * a4 * a3 * a2 * a2 * a1 * a0
            + _18 * a4 * a3 * a2 * a1 * a1 * a1
            + _16 * a4 * a2 * a2 * a2 * a2 * a0
            - _4 * a4 * a2 * a2 * a2 * a1 * a1
            - _27 * a3 * a3 * a3 * a3 * a0 * a0
            + _18 * a3 * a3 * a3 * a2 * a1 * a0
            - _4 * a3 * a3 * a3 * a1 * a1 * a1
            - _4 * a3 * a3 * a2 * a2 * a2 * a0
            + a3 * a3 * a2 * a2 * a1 * a1;
        let pp = _8 * a4 * a2 - _3 * a3 * a3;
        let rr = a3 * a3 * a3 + _8 * a4 * a4 * a1 - _4 * a4 * a3 * a2;
        let delta0 = a2 * a2 - _3 * a3 * a1 + _12 * a4 * a0;
        let dd = _64 * a4 * a4 * a4 * a0 - _16 * a4 * a4 * a2 * a2 + _16 * a4 * a3 * a3 * a2
            - _16 * a4 * a4 * a3 * a1
            - _3 * a3 * a3 * a3 * a3;

        // Handle special cases
        let triple_root = dbg!(discriminant) == F::zero() && dbg!(delta0) == F::zero();
        let quadruple_root = triple_root && dbg!(dd) == F::zero();
        let no_roots = discriminant == F::zero() && dd == F::zero() && dbg!(pp) > F::zero() && dbg!(rr) == F::zero();
        if dbg!(quadruple_root) {
            // Wiki: all four roots are equal
            Roots::One([-a3 / (_4 * a4)])
        } else if dbg!(triple_root) {
            // Wiki: At least three roots are equal to each other
            // x0 is the unique root of the remainder of the Euclidean division of the quartic by its second derivative
            //
            // Solved by SymPy (ra is the desired reminder)
            // a, b, c, d, e = symbols('a,b,c,d,e')
            // f=a*x**4+b*x**3+c*x**2+d*x+e     // Quartic polynom
            // g=6*a*x**2+3*b*x+c               // Second derivative
            // q, r = div(f, g)                 // SymPy only finds the highest power
            // simplify(f-(q*g+r)) == 0         // Verify the first division
            // qa, ra = div(r/a,g/a)            // Workaround to get the second division
            // simplify(f-((q+qa)*g+ra*a)) == 0 // Verify the second division
            // solve(ra,x)
            // ----- yields
            // (−72*a^2*e+10*a*c^2−3*b^2*c)/(9*(8*a^2*d−4*a*b*c+b^3))
            let x0 = (-_72 * a4 * a4 * a0 + _10 * a4 * a2 * a2 - _3 * a3 * a3 * a2)
                / (_9 * (_8 * a4 * a4 * a1 - _4 * a4 * a3 * a2 + a3 * a3 * a3));
            let roots = dbg!(Roots::One([x0]));
            roots.add_new_root(dbg!(-(a3 / a4 + _3 * x0)))
        } else if dbg!(no_roots) {
            // Wiki: two complex conjugate double roots
            Roots::No([])
        } else {
            // Depressed quartic
            // https://en.wikipedia.org/wiki/Quartic_function#Converting_to_a_depressed_quartic

            // a4*x^4 + a3*x^3 + a2*x^2 + a1*x + a0 = 0 => x^4 + a*x^3 + b*x^2 + c*x + d = 0.
            let (a, b, c, d) = (a3 / a4, a2 / a4, a1 / a4, a0 / a4);
            // x^4 + a*x^3 + b*x^2 + c*x + d = 0 => y^4 + p*y^2 + q*y + r.
            let a_pow_2 = a * a;
            let a_pow_3 = a_pow_2 * a;
            let a_pow_4 = a_pow_2 * a_pow_2;
            let subst = -a3 / (F::four() * a4);
            let p = b - F::three() * a_pow_2 / _8;
            let q = a_pow_3 / _8 - a * b / F::two() + c;
            let r = d - F::three() * a_pow_4 / _256 - c * a / F::four() + a_pow_2 * b / _16;

            let mut roots = Roots::No([]);
            for x in super::quartic_depressed::find_roots_quartic_depressed(dbg!(p), dbg!(q), dbg!(r))
                .as_ref()
                .iter()
            {
                roots = roots.add_new_root(*x + subst);
            }
            roots
        }
    }
}

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

    #[test]
    fn test_find_roots_quartic() {
        assert_eq!(find_roots_quartic(1f32, 0f32, 0f32, 0f32, 0f32), Roots::One([0f32]));
        assert_eq!(find_roots_quartic(1f64, 0f64, 0f64, 0f64, -1f64), Roots::Two([-1f64, 1f64]));
        assert_eq!(
            find_roots_quartic(1f64, -10f64, 35f64, -50f64, 24f64),
            Roots::Four([1f64, 2f64, 3f64, 4f64])
        );

        match find_roots_quartic(3f64, 5f64, -5f64, -5f64, 2f64) {
            Roots::Four(x) => {
                assert_float_array_eq!(2e-15f64, x, [-2f64, -1f64, 0.33333333333333333f64, 1f64]);
            }
            _ => {
                assert!(false);
            }
        }

        match find_roots_quartic(3f32, 5f32, -5f32, -5f32, 2f32) {
            Roots::Four(x) => {
                assert_float_array_eq!(5e-7, x, [-2f32, -1f32, 0.33333333333333333f32, 1f32]);
            }
            _ => {
                assert!(false);
            }
        }
    }

    #[test]
    fn test_find_roots_quartic_tim_luecke() {
        assert_eq!(
            find_roots_quartic(-14.0625f64, -3.75f64, 29.75f64, 4.0f64, -16.0f64),
            Roots::Two([-1.1016117f64, 0.96827835f64])
        );
        assert_eq!(
            find_roots_quartic(-14.0625f32, -3.75f32, 29.75f32, 4.0f32, -16.0f32),
            Roots::Two([-1.1016117f32, 0.96827835f32])
        );
    }

    #[test]
    fn test_find_roots_quartic_triple_root() {
        // (x+3)(3x-1)^3 == 27 x^4 + 54 x^3 - 72 x^2 + 26 x - 3
        assert_eq!(
            find_roots_quartic(27f64, 54f64, -72f64, 26f64, -3f64),
            Roots::Two([-3.0f64, 0.3333333333333333f64])
        );
        assert_eq!(
            find_roots_quartic(27f32, 54f32, -72f32, 26f32, -3f32),
            Roots::Two([-3.0f32, 0.33333333f32])
        );
    }

    #[test]
    fn test_find_roots_quartic_quadruple_root() {
        // (7x+2)^4 == 2401 x^4 + 2744 x^3 + 1176 x^2 + 224 x + 16
        assert_eq!(
            find_roots_quartic(2401f64, 2744f64, 1176f64, 224f64, 16f64),
            Roots::One([-0.2857142857142857f64])
        );
        // 32-bit floating point is never accurate
        assert_eq!(
            find_roots_quartic(2401f32, 2744f32, 1176f32, 224f32, 16f32),
            Roots::Two([-0.29361892f32, -0.27799278f32])
        );
    }
}
<|endoftext|>


<commit_before>use ::chrono::{DateTime,Utc};
use ::std::fmt;
use ::std;
use ::std::borrow::Borrow;
use ::std::hash::{Hash, Hasher};
use ::std::any::Any;

// TODO: uncomment this when support lands in rustc for improved safety
//#[repr(transparent)]
#[derive(Copy,Clone,Debug)]
pub struct AsciiFolding<S: ?Sized>(S);

impl<S> AsciiFolding<S> {
    pub fn new(s: S) -> Self {
        AsciiFolding(s)
    }
}

impl<S: AsRef<str>> AsRef<str> for AsciiFolding<S> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}

impl<S: fmt::Display> fmt::Display for AsciiFolding<S> {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl<S: ?Sized> AsciiFolding<S> {
    pub fn borrowed(s: &S) -> &Self {
        unsafe { &*(s as *const S as *const AsciiFolding<S>) }
    }
}

impl Borrow<AsciiFolding<str>> for AsciiFolding<String> {
    #[inline]
    fn borrow(&self) -> &AsciiFolding<str> {
        AsciiFolding::borrowed(self.0.borrow())
    }
}

impl<S: Borrow<str> + ?Sized> PartialEq for AsciiFolding<S> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0.borrow().eq_ignore_ascii_case(other.0.borrow())
    }
}

impl<S: Borrow<str> + ?Sized> Eq for AsciiFolding<S> {}

impl<S: Borrow<str> + ?Sized> Hash for AsciiFolding<S> {
    #[inline]
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        for byte in self.0.borrow().bytes().map(|b| b.to_ascii_lowercase()) {
            hasher.write_u8(byte);
        }
    }
}

#[test]
fn test_hashmap_key() {
    let mut m = std::collections::HashMap::new();
    m.insert(AsciiFolding("v".to_owned()), "value");

    m.get(AsciiFolding::borrowed("v")).unwrap();
}

/// Debounce filter with interval adjustment and prediction (not Sync/Send)
pub struct Debounce{
    interval: Ticks,
    next: Ticks,
    ticks_per_second: Ticks,
}
impl Debounce{
    pub fn new(interval: ::chrono::Duration, clock: &dyn AppClock) -> Debounce{
        let mut d= Debounce{
            interval: 0,
            next: 0,
            ticks_per_second: clock.ticks_per_second()
        };
        d.set_interval(interval);
        d
    }
    pub fn interval(&self) -> ::chrono::Duration{
        ::chrono::Duration::nanoseconds((self.interval as u128 * self.ticks_per_second as u128 / 1000000000) as i64)
    }
    pub fn set_interval(&mut self, interval: ::chrono::Duration){
        self.interval = (interval.num_nanoseconds().unwrap_or(0) as u128 * self.ticks_per_second as u128 / 1000000000) as u64;
    }

    pub fn set_interval_if_stopped(&mut self, interval: ::chrono::Duration){
        if self.interval == 0{
            self.set_interval(interval);
        }
    }

    pub fn stop(&mut self){
        self.interval = 0;
    }
    pub fn next(&self) -> Ticks{
        self.next
    }
    pub fn allow(&mut self, clock: &dyn AppClock) -> bool{
        if self.interval <= 0{
            return false;
        } else {
            let now = clock.get_timestamp_ticks();
            if now > self.next{
                self.next = now + self.interval;
                true
            }else {
                false
            }
        }
    }
}

/// Like .for_each, but for Option instead of Iterator
pub trait DoSome{
    type Item;
    fn do_some<F>(&mut self, f: F) -> () where F: FnMut(&mut Self::Item) -> ();
}
impl<T> DoSome for Option<T>{
    type Item = T;
    fn do_some<F>(&mut self, mut f: F) -> () where
        F: FnMut(&mut Self::Item) -> () {
        if let Some(ref mut v) = *self{
            f(v)
        }
    }
}

/// A time, ticks, and build date source for
pub trait AppClock: Sync + Send{
    fn get_timestamp_ticks(&self) -> u64;
    fn ticks_per_second(&self) -> u64;
    fn get_build_date(&self) -> DateTime<Utc>;
    fn get_utc_now(&self) -> DateTime<Utc>;
}

#[derive(Debug,Clone,Copy)]
pub struct DefaultClock{
    pub build_date: DateTime<Utc>
}
impl AppClock for DefaultClock{
    fn get_timestamp_ticks(&self) -> u64 {
        ::timeywimey::precise_time_ns()
    }

    fn ticks_per_second(&self) -> u64 {
        100000000
    }

    fn get_build_date(&self) -> DateTime<Utc> {
        self.build_date
    }

    fn get_utc_now(&self) -> DateTime<Utc> {
        Utc::now()
    }
}

/// Allow a &str or String panic value to be printed
pub struct PanicFormatter<'a>(pub &'a dyn Any);
impl<'a> std::fmt::Display for PanicFormatter<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        if let Some(str) = self.0.downcast_ref::<String>() {
            write!(f, "panicked: {}\n", str)?;
        } else if let Some(str) = self.0.downcast_ref::<&str>() {
            write!(f, "panicked: {}\n", str)?;
        }
        Ok(())
    }
}

pub type Ticks = u64;

#[derive(Debug,PartialEq)]
pub enum IssueKind{ Error, Warning, Message}

/// Represents a problem that should be relayed in diagnostic reports and possibly printed to stderr
/// Deduplicated by hash of the message (detail is excluded)
#[derive(Debug, PartialEq)]
pub struct Issue{
    hash: u64,
    source: &'static str,
    message: String,
    detail: String,
    kind: IssueKind,
}

impl Issue{
    pub fn new(kind: IssueKind, message: String, detail:String, source: &'static str) -> Self{
        let hash = ::hashing::hash_64(message.as_bytes());
        Issue{
            hash,
            message,
            detail,
            kind,
            source
        }
    }
    pub fn hash(&self) -> u64{
        self.hash
    }
}

impl fmt::Display for Issue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{} {:?}: {}\n{}\n", self.source, self.kind, self.message, self.detail)
    }
}
<commit_msg>Improve safety of AsciiFolding with repr(transparent)<commit_after>use ::chrono::{DateTime,Utc};
use ::std::fmt;
use ::std;
use ::std::borrow::Borrow;
use ::std::hash::{Hash, Hasher};
use ::std::any::Any;

#[repr(transparent)]
#[derive(Copy,Clone,Debug)]
pub struct AsciiFolding<S: ?Sized>(S);

impl<S> AsciiFolding<S> {
    pub fn new(s: S) -> Self {
        AsciiFolding(s)
    }
}

impl<S: AsRef<str>> AsRef<str> for AsciiFolding<S> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.0.as_ref()
    }
}

impl<S: fmt::Display> fmt::Display for AsciiFolding<S> {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl<S: ?Sized> AsciiFolding<S> {
    pub fn borrowed(s: &S) -> &Self {
        unsafe { &*(s as *const S as *const AsciiFolding<S>) }
    }
}

impl Borrow<AsciiFolding<str>> for AsciiFolding<String> {
    #[inline]
    fn borrow(&self) -> &AsciiFolding<str> {
        AsciiFolding::borrowed(self.0.borrow())
    }
}

impl<S: Borrow<str> + ?Sized> PartialEq for AsciiFolding<S> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0.borrow().eq_ignore_ascii_case(other.0.borrow())
    }
}

impl<S: Borrow<str> + ?Sized> Eq for AsciiFolding<S> {}

impl<S: Borrow<str> + ?Sized> Hash for AsciiFolding<S> {
    #[inline]
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        for byte in self.0.borrow().bytes().map(|b| b.to_ascii_lowercase()) {
            hasher.write_u8(byte);
        }
    }
}

#[test]
fn test_hashmap_key() {
    let mut m = std::collections::HashMap::new();
    m.insert(AsciiFolding("v".to_owned()), "value");

    m.get(AsciiFolding::borrowed("v")).unwrap();
}

/// Debounce filter with interval adjustment and prediction (not Sync/Send)
pub struct Debounce{
    interval: Ticks,
    next: Ticks,
    ticks_per_second: Ticks,
}
impl Debounce{
    pub fn new(interval: ::chrono::Duration, clock: &dyn AppClock) -> Debounce{
        let mut d= Debounce{
            interval: 0,
            next: 0,
            ticks_per_second: clock.ticks_per_second()
        };
        d.set_interval(interval);
        d
    }
    pub fn interval(&self) -> ::chrono::Duration{
        ::chrono::Duration::nanoseconds((self.interval as u128 * self.ticks_per_second as u128 / 1000000000) as i64)
    }
    pub fn set_interval(&mut self, interval: ::chrono::Duration){
        self.interval = (interval.num_nanoseconds().unwrap_or(0) as u128 * self.ticks_per_second as u128 / 1000000000) as u64;
    }

    pub fn set_interval_if_stopped(&mut self, interval: ::chrono::Duration){
        if self.interval == 0{
            self.set_interval(interval);
        }
    }

    pub fn stop(&mut self){
        self.interval = 0;
    }
    pub fn next(&self) -> Ticks{
        self.next
    }
    pub fn allow(&mut self, clock: &dyn AppClock) -> bool{
        if self.interval <= 0{
            return false;
        } else {
            let now = clock.get_timestamp_ticks();
            if now > self.next{
                self.next = now + self.interval;
                true
            }else {
                false
            }
        }
    }
}

/// Like .for_each, but for Option instead of Iterator
pub trait DoSome{
    type Item;
    fn do_some<F>(&mut self, f: F) -> () where F: FnMut(&mut Self::Item) -> ();
}
impl<T> DoSome for Option<T>{
    type Item = T;
    fn do_some<F>(&mut self, mut f: F) -> () where
        F: FnMut(&mut Self::Item) -> () {
        if let Some(ref mut v) = *self{
            f(v)
        }
    }
}

/// A time, ticks, and build date source for
pub trait AppClock: Sync + Send{
    fn get_timestamp_ticks(&self) -> u64;
    fn ticks_per_second(&self) -> u64;
    fn get_build_date(&self) -> DateTime<Utc>;
    fn get_utc_now(&self) -> DateTime<Utc>;
}

#[derive(Debug,Clone,Copy)]
pub struct DefaultClock{
    pub build_date: DateTime<Utc>
}
impl AppClock for DefaultClock{
    fn get_timestamp_ticks(&self) -> u64 {
        ::timeywimey::precise_time_ns()
    }

    fn ticks_per_second(&self) -> u64 {
        100000000
    }

    fn get_build_date(&self) -> DateTime<Utc> {
        self.build_date
    }

    fn get_utc_now(&self) -> DateTime<Utc> {
        Utc::now()
    }
}

/// Allow a &str or String panic value to be printed
pub struct PanicFormatter<'a>(pub &'a dyn Any);
impl<'a> std::fmt::Display for PanicFormatter<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        if let Some(str) = self.0.downcast_ref::<String>() {
            write!(f, "panicked: {}\n", str)?;
        } else if let Some(str) = self.0.downcast_ref::<&str>() {
            write!(f, "panicked: {}\n", str)?;
        }
        Ok(())
    }
}

pub type Ticks = u64;

#[derive(Debug,PartialEq)]
pub enum IssueKind{ Error, Warning, Message}

/// Represents a problem that should be relayed in diagnostic reports and possibly printed to stderr
/// Deduplicated by hash of the message (detail is excluded)
#[derive(Debug, PartialEq)]
pub struct Issue{
    hash: u64,
    source: &'static str,
    message: String,
    detail: String,
    kind: IssueKind,
}

impl Issue{
    pub fn new(kind: IssueKind, message: String, detail:String, source: &'static str) -> Self{
        let hash = ::hashing::hash_64(message.as_bytes());
        Issue{
            hash,
            message,
            detail,
            kind,
            source
        }
    }
    pub fn hash(&self) -> u64{
        self.hash
    }
}

impl fmt::Display for Issue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{} {:?}: {}\n{}\n", self.source, self.kind, self.message, self.detail)
    }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 Saurav Sachidanand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

extern crate gcc;
use std::env;
use std::process::Command;

fn main() {
	let mut cfg = gcc::Config::new();
    let env = env::var("TARGET").unwrap();

    cfg.file("nfd/src/nfd_common.c");

    if env.contains("darwin") {
    	cfg.file("nfd/src/nfd_cocoa.m");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=framework=AppKit");
    } else if env.contains("windows") {
    	cfg.file("nfd/src/nfd_win.cpp");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=ole32");
    } else {
    	let pkg_output = Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0").output();
    	match pkg_output {
    		Ok(output) => {
    			let t = String::from_utf8(output.stdout).unwrap();
    			let flags = t.split(" ");
    			for flag in flags {
    				if flag != "\n" && flag != "" {
    					cfg.flag(flag);
    				}
    			}
    		}
    		_ => (),
    	}
    	cfg.file("nfd/src/nfd_gtk.c");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=gdk-3");
        println!("cargo:rustc-link-lib=gtk-3");
        println!("cargo:rustc-link-lib=glib-2.0");
        println!("cargo:rustc-link-lib=gobject-2.0");
    }
}
<commit_msg>Added glib-2.0 flags<commit_after>/*
Copyright (c) 2016 Saurav Sachidanand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

extern crate gcc;
use std::env;
use std::process::Command;

fn main() {
	let mut cfg = gcc::Config::new();
    let env = env::var("TARGET").unwrap();

    cfg.file("nfd/src/nfd_common.c");

    if env.contains("darwin") {
    	cfg.file("nfd/src/nfd_cocoa.m");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=framework=AppKit");
    } else if env.contains("windows") {
    	cfg.file("nfd/src/nfd_win.cpp");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=ole32");
    } else {
    	let pkg_output = Command::new("pkg-config")
			.arg("--cflags")
			.arg("gtk+-3.0")
			.arg("glib-2.0")
			.arg("--libs")
			.arg("glib-2.0")
			.output();
    	match pkg_output {
    		Ok(output) => {
    			let t = String::from_utf8(output.stdout).unwrap();
    			let flags = t.split(" ");
    			for flag in flags {
    				if flag != "\n" && flag != "" {
    					cfg.flag(flag);
    				}
    			}
    		}
    		_ => (),
    	}
    	cfg.file("nfd/src/nfd_gtk.c");
    	cfg.compile("libnfd.a");
        println!("cargo:rustc-link-lib=gdk-3");
        println!("cargo:rustc-link-lib=gtk-3");
        println!("cargo:rustc-link-lib=glib-2.0");
        println!("cargo:rustc-link-lib=gobject-2.0");
    }
}
<|endoftext|>