<commit_before>use crate::cpp_ffi_data::{
    CppFfiArgumentMeaning, CppFfiFunctionKind, CppFfiType, CppFieldAccessorType,
    CppTypeConversionToFfi, QtSlotWrapper,
};
use crate::cpp_function::ReturnValueAllocationPlace;
use crate::cpp_type::CppType;
use itertools::Itertools;
use ritual_common::errors::{bail, unexpected, Result};
use ritual_common::file_utils::{create_file, path_to_str};
use ritual_common::utils::get_command_output;
use ritual_common::utils::MapIfOk;

use crate::cpp_ffi_data::CppFfiFunction;
use crate::cpp_type::CppPointerLikeTypeKind;
use crate::database::CppDatabaseItem;
use crate::database::CppFfiItemKind;
use crate::rust_info::RustDatabase;
use crate::rust_info::RustItemKind;
use crate::rust_info::RustStructKind;
use std::io::Write;
use std::iter::once;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Generates function name, return type and arguments list
/// as it appears in both function declaration and implementation.
fn function_signature(method: &CppFfiFunction) -> Result<String> {
    let mut arg_texts = Vec::new();
    for arg in &method.arguments {
        arg_texts.push(arg.to_cpp_code()?);
    }
    let name_with_args = format!("{}({})", method.path.to_cpp_code()?, arg_texts.join(", "));
    let return_type = &method.return_type.ffi_type;
    let r = if let CppType::FunctionPointer(..) = return_type {
        return_type.to_cpp_code(Some(&name_with_args))?
    } else {
        format!("{} {}", return_type.to_cpp_code(None)?, name_with_args)
    };
    Ok(r)
}

/// Generates code for a Qt slot wrapper
fn qt_slot_wrapper(wrapper: &QtSlotWrapper) -> Result<String> {
    let func_type = CppType::FunctionPointer(wrapper.function_type.clone());
    let method_args = wrapper
        .arguments
        .iter()
        .enumerate()
        .map_if_ok(|(num, t)| -> Result<_> {
            Ok(format!("{} arg{}", t.original_type.to_cpp_code(None)?, num))
        })?
        .join(", ");
    let func_args = once("m_data".to_string())
        .chain(
            wrapper
                .arguments
                .iter()
                .enumerate()
                .map_if_ok(|(num, t)| convert_type_to_ffi(t, format!("arg{}", num)))?,
        )
        .join(", ");
    Ok(format!(
        include_str!("../templates/c_lib/qt_slot_wrapper.h"),
        class_name = &wrapper.class_path,
        func_arg = func_type.to_cpp_code(Some("func"))?,
        func_field = func_type.to_cpp_code(Some("m_func"))?,
        method_args = method_args,
        func_args = func_args
    ))
}

/// Generates code that wraps `expression` of type `type1.original_type` and
/// converts it to type `type1.ffi_type`
fn convert_type_to_ffi(type1: &CppFfiType, expression: String) -> Result<String> {
    Ok(match type1.conversion {
        CppTypeConversionToFfi::NoChange => expression,
        CppTypeConversionToFfi::ValueToPointer => format!(
            "new {}({})",
            type1.original_type.to_cpp_code(None)?,
            expression
        ),
        CppTypeConversionToFfi::ReferenceToPointer => format!("&{}", expression),
        CppTypeConversionToFfi::QFlagsToUInt => format!("uint({})", expression),
    })
}

/// Wraps `expression` returned by the original C++ method to
/// convert it to return type of the FFI method.
fn convert_return_type(method: &CppFfiFunction, expression: String) -> Result<String> {
    let mut result = expression;
    match method.return_type.conversion {
        CppTypeConversionToFfi::NoChange => {}
        CppTypeConversionToFfi::ValueToPointer => {
            match method.allocation_place {
                ReturnValueAllocationPlace::Stack => {
                    unexpected!("stack allocated wrappers are expected to return void");
                }
                ReturnValueAllocationPlace::NotApplicable => {
                    unexpected!("ValueToPointer conflicts with NotApplicable");
                }
                ReturnValueAllocationPlace::Heap => {
                    // constructors are said to return values in parse result,
                    // but in reality we use `new` which returns a pointer,
                    // so no conversion is necessary for constructors.
                    if !method
                        .kind
                        .cpp_function()
                        .map(|m| m.is_constructor())
                        .unwrap_or(false)
                    {
                        result = format!(
                            "new {}({})",
                            method.return_type.original_type.to_cpp_code(None)?,
                            result
                        );
                    }
                }
            }
        }
        CppTypeConversionToFfi::ReferenceToPointer => {
            result = format!("&{}", result);
        }
        CppTypeConversionToFfi::QFlagsToUInt => {
            result = format!("uint({})", result);
        }
    }

    if method.allocation_place == ReturnValueAllocationPlace::Stack
        && !method
            .kind
            .cpp_function()
            .map(|m| m.is_constructor())
            .unwrap_or(false)
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue)
        {
            result = format!(
                "new({}) {}({})",
                arg.name,
                method.return_type.original_type.to_cpp_code(None)?,
                result
            );
        }
    }
    Ok(result)
}

/// Generates code for values passed to the original C++ method.
fn arguments_values(method: &CppFfiFunction) -> Result<String> {
    let r: Vec<_> = method
        .arguments
        .iter()
        .filter(|arg| arg.meaning.is_argument())
        .map_if_ok(|argument| -> Result<_> {
            let mut result = argument.name.clone();
            match argument.argument_type.conversion {
                CppTypeConversionToFfi::ValueToPointer
                | CppTypeConversionToFfi::ReferenceToPointer => result = format!("*{}", result),
                CppTypeConversionToFfi::NoChange => {}
                CppTypeConversionToFfi::QFlagsToUInt => {
                    let type_text = if let CppType::PointerLike {
                        ref kind,
                        ref is_const,
                        ref target,
                    } = argument.argument_type.original_type
                    {
                        if *kind == CppPointerLikeTypeKind::Reference && *is_const {
                            target.to_cpp_code(None)?
                        } else {
                            bail!("Unsupported original type for QFlagsToUInt conversion");
                        }
                    } else {
                        argument.argument_type.original_type.to_cpp_code(None)?
                    };
                    result = format!("{}({})", type_text, result);
                }
            }
            Ok(result)
        })?;
    Ok(r.join(", "))
}

/// Generates code for the value returned by the FFI method.
#[allow(clippy::collapsible_if)]
fn returned_expression(method: &CppFfiFunction) -> Result<String> {
    let result = if method
        .kind
        .cpp_function()
        .map(|m| m.is_destructor())
        .unwrap_or(false)
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::This)
        {
            format!("c2r_call_destructor({})", arg.name)
        } else {
            unexpected!("no this arg in destructor");
        }
    } else {
        let mut is_field_accessor = false;

        let result_without_args =
            if let Some(cpp_function) = method.kind.cpp_function().filter(|m| m.is_constructor()) {
                match method.allocation_place {
                    ReturnValueAllocationPlace::Stack => {
                        if let Some(arg) = method
                            .arguments
                            .iter()
                            .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue)
                        {
                            format!(
                                "new({}) {}",
                                arg.name,
                                cpp_function.class_type().unwrap().to_cpp_code()?
                            )
                        } else {
                            unexpected!("return value argument not found\n{:?}", method);
                        }
                    }
                    ReturnValueAllocationPlace::Heap => {
                        format!("new {}", cpp_function.class_type().unwrap().to_cpp_code()?)
                    }
                    ReturnValueAllocationPlace::NotApplicable => {
                        unexpected!("NotApplicable in constructor");
                    }
                }
            } else {
                // TODO: scope specifier should probably be stored in a field `cpp_full_name` of `CppFFiMethod`
                let scope_specifier = if let Some(ref cpp_function) =
                    method.kind.cpp_function().filter(|m| m.is_static_member())
                {
                    // static method
                    format!("{}::", cpp_function.class_type().unwrap().to_cpp_code()?)
                } else if let Some(ref field) = method.kind.cpp_field().filter(|f| f.is_static) {
                    // static field
                    format!(
                        "{}::",
                        field
                            .path
                            .parent()
                            .expect("field path must have parent")
                            .to_cpp_code()?
                    )
                } else {
                    // regular member method/field or a free function
                    if let Some(arg) = method
                        .arguments
                        .iter()
                        .find(|x| x.meaning == CppFfiArgumentMeaning::This)
                    {
                        format!("{}->", arg.name)
                    } else {
                        "".to_string()
                    }
                };
                match method.kind {
                    CppFfiFunctionKind::FieldAccessor {
                        ref accessor_type,
                        ref field,
                    } => {
                        is_field_accessor = true;
                        if accessor_type == &CppFieldAccessorType::Setter {
                            format!(
                                "{}{} = {}",
                                scope_specifier,
                                field.path.last().name,
                                arguments_values(method)?
                            )
                        } else {
                            format!("{}{}", scope_specifier, field.path.last().name)
                        }
                    }
                    CppFfiFunctionKind::Function {
                        ref cpp_function, ..
                    } => format!("{}{}", scope_specifier, cpp_function.path.to_cpp_code()?),
                }
            };
        if is_field_accessor {
            result_without_args
        } else {
            format!("{}({})", result_without_args, arguments_values(method)?)
        }
    };
    convert_return_type(method, result)
}

/// Generates body of the FFI method implementation.
fn source_body(method: &CppFfiFunction) -> Result<String> {
    if method
        .kind
        .cpp_function()
        .map(|m| m.is_destructor())
        .unwrap_or(false)
        && method.allocation_place == ReturnValueAllocationPlace::Heap
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::This)
        {
            Ok(format!("delete {};\n", arg.name))
        } else {
            panic!("Error: no this argument found\n{:?}", method);
        }
    } else {
        Ok(format!(
            "{}{};\n",
            if method.return_type.ffi_type.is_void() {
                ""
            } else {
                "return "
            },
            returned_expression(&method)?
        ))
    }
}

/// Generates implementation of the FFI method for the source file.
pub fn function_implementation(method: &CppFfiFunction) -> Result<String> {
    Ok(format!(
        "C2R_EXPORT {} {{\n  {}}}\n\n",
        function_signature(method)?,
        source_body(&method)?
    ))
}

/// Generates a source file with the specified FFI methods.
pub fn generate_cpp_file(
    data: &[CppDatabaseItem],
    file_path: &Path,
    global_header_name: &str,
) -> Result<()> {
    //    let cpp_path = self
    //      .lib_path
    //      .join("src")
    //      .join(format!("{}_{}.cpp", &self.lib_name, data.name));

    let mut cpp_file = create_file(file_path)?;
    cpp_file.write(format!("#include \"{}\"\n", global_header_name))?;

    let mut any_slot_wrappers = false;
    for item in data {
        for ffi_item in &item.ffi_items {
            match ffi_item.kind {
                CppFfiItemKind::Function(ref cpp_ffi_function) => {
                    // TODO: write less extern C
                    cpp_file.write("extern \"C\" {\n\n")?;
                    cpp_file.write(function_implementation(cpp_ffi_function)?)?;
                    cpp_file.write("\n} // extern \"C\"\n\n")?;
                }
                CppFfiItemKind::QtSlotWrapper(ref qt_slot_wrapper) => {
                    any_slot_wrappers = true;
                    cpp_file.write(self::qt_slot_wrapper(qt_slot_wrapper)?)?;
                }
            }
        }
    }
    if any_slot_wrappers {
        let moc_output = get_command_output(Command::new("moc").arg("-i").arg(file_path))?;
        cpp_file.write(format!(
            "// start of MOC generated code\n{}\n// end of MOC generated code\n",
            moc_output
        ))?;
    }
    Ok(())
}

/// Generates a C++ program that determines sizes of target C++ types
/// on the current platform and outputs the Rust code for `sized_types.rs` module
/// to the standard output.
pub fn generate_cpp_type_size_requester(
    rust_database: &RustDatabase,
    include_directives: &[PathBuf],
    mut output: impl Write,
) -> Result<()> {
    for dir in include_directives {
        writeln!(output, "#include <{}>", path_to_str(dir)?)?;
    }
    writeln!(output, "#include <stdio.h>\n\nint main() {{")?;

    for item in &rust_database.items {
        if let RustItemKind::Struct(ref data) = item.kind {
            if let RustStructKind::SizedType(ref cpp_path) = data.kind {
                let cpp_path_code = cpp_path.to_cpp_code()?;

                writeln!(
                    output,
                    "printf(\"#[repr(C, align(%d))]\\n\", alignof({}));",
                    cpp_path_code
                )?;

                writeln!(
                    output,
                    "printf(\"pub struct {}([u8; %d]);\\n\\n\", sizeof({}));",
                    data.path.last(),
                    cpp_path_code
                )?;
            }
        }
    }

    writeln!(output, "}}")?;
    Ok(())
}
<commit_msg>simplify and fix paths in cpp code generator<commit_after>use crate::cpp_ffi_data::{
    CppFfiArgumentMeaning, CppFfiFunctionKind, CppFfiType, CppFieldAccessorType,
    CppTypeConversionToFfi, QtSlotWrapper,
};
use crate::cpp_function::ReturnValueAllocationPlace;
use crate::cpp_type::CppType;
use itertools::Itertools;
use ritual_common::errors::{bail, unexpected, Result};
use ritual_common::file_utils::{create_file, path_to_str};
use ritual_common::utils::get_command_output;
use ritual_common::utils::MapIfOk;

use crate::cpp_ffi_data::CppFfiFunction;
use crate::cpp_type::CppPointerLikeTypeKind;
use crate::database::CppDatabaseItem;
use crate::database::CppFfiItemKind;
use crate::rust_info::RustDatabase;
use crate::rust_info::RustItemKind;
use crate::rust_info::RustStructKind;
use std::io::Write;
use std::iter::once;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Generates function name, return type and arguments list
/// as it appears in both function declaration and implementation.
fn function_signature(method: &CppFfiFunction) -> Result<String> {
    let mut arg_texts = Vec::new();
    for arg in &method.arguments {
        arg_texts.push(arg.to_cpp_code()?);
    }
    let name_with_args = format!("{}({})", method.path.to_cpp_code()?, arg_texts.join(", "));
    let return_type = &method.return_type.ffi_type;
    let r = if let CppType::FunctionPointer(..) = return_type {
        return_type.to_cpp_code(Some(&name_with_args))?
    } else {
        format!("{} {}", return_type.to_cpp_code(None)?, name_with_args)
    };
    Ok(r)
}

/// Generates code for a Qt slot wrapper
fn qt_slot_wrapper(wrapper: &QtSlotWrapper) -> Result<String> {
    let func_type = CppType::FunctionPointer(wrapper.function_type.clone());
    let method_args = wrapper
        .arguments
        .iter()
        .enumerate()
        .map_if_ok(|(num, t)| -> Result<_> {
            Ok(format!("{} arg{}", t.original_type.to_cpp_code(None)?, num))
        })?
        .join(", ");
    let func_args = once("m_data".to_string())
        .chain(
            wrapper
                .arguments
                .iter()
                .enumerate()
                .map_if_ok(|(num, t)| convert_type_to_ffi(t, format!("arg{}", num)))?,
        )
        .join(", ");
    Ok(format!(
        include_str!("../templates/c_lib/qt_slot_wrapper.h"),
        class_name = &wrapper.class_path,
        func_arg = func_type.to_cpp_code(Some("func"))?,
        func_field = func_type.to_cpp_code(Some("m_func"))?,
        method_args = method_args,
        func_args = func_args
    ))
}

/// Generates code that wraps `expression` of type `type1.original_type` and
/// converts it to type `type1.ffi_type`
fn convert_type_to_ffi(type1: &CppFfiType, expression: String) -> Result<String> {
    Ok(match type1.conversion {
        CppTypeConversionToFfi::NoChange => expression,
        CppTypeConversionToFfi::ValueToPointer => format!(
            "new {}({})",
            type1.original_type.to_cpp_code(None)?,
            expression
        ),
        CppTypeConversionToFfi::ReferenceToPointer => format!("&{}", expression),
        CppTypeConversionToFfi::QFlagsToUInt => format!("uint({})", expression),
    })
}

/// Wraps `expression` returned by the original C++ method to
/// convert it to return type of the FFI method.
fn convert_return_type(method: &CppFfiFunction, expression: String) -> Result<String> {
    let mut result = expression;
    match method.return_type.conversion {
        CppTypeConversionToFfi::NoChange => {}
        CppTypeConversionToFfi::ValueToPointer => {
            match method.allocation_place {
                ReturnValueAllocationPlace::Stack => {
                    unexpected!("stack allocated wrappers are expected to return void");
                }
                ReturnValueAllocationPlace::NotApplicable => {
                    unexpected!("ValueToPointer conflicts with NotApplicable");
                }
                ReturnValueAllocationPlace::Heap => {
                    // constructors are said to return values in parse result,
                    // but in reality we use `new` which returns a pointer,
                    // so no conversion is necessary for constructors.
                    if !method
                        .kind
                        .cpp_function()
                        .map(|m| m.is_constructor())
                        .unwrap_or(false)
                    {
                        result = format!(
                            "new {}({})",
                            method.return_type.original_type.to_cpp_code(None)?,
                            result
                        );
                    }
                }
            }
        }
        CppTypeConversionToFfi::ReferenceToPointer => {
            result = format!("&{}", result);
        }
        CppTypeConversionToFfi::QFlagsToUInt => {
            result = format!("uint({})", result);
        }
    }

    if method.allocation_place == ReturnValueAllocationPlace::Stack
        && !method
            .kind
            .cpp_function()
            .map(|m| m.is_constructor())
            .unwrap_or(false)
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue)
        {
            result = format!(
                "new({}) {}({})",
                arg.name,
                method.return_type.original_type.to_cpp_code(None)?,
                result
            );
        }
    }
    Ok(result)
}

/// Generates code for values passed to the original C++ method.
fn arguments_values(method: &CppFfiFunction) -> Result<String> {
    let r: Vec<_> = method
        .arguments
        .iter()
        .filter(|arg| arg.meaning.is_argument())
        .map_if_ok(|argument| -> Result<_> {
            let mut result = argument.name.clone();
            match argument.argument_type.conversion {
                CppTypeConversionToFfi::ValueToPointer
                | CppTypeConversionToFfi::ReferenceToPointer => result = format!("*{}", result),
                CppTypeConversionToFfi::NoChange => {}
                CppTypeConversionToFfi::QFlagsToUInt => {
                    let type_text = if let CppType::PointerLike {
                        ref kind,
                        ref is_const,
                        ref target,
                    } = argument.argument_type.original_type
                    {
                        if *kind == CppPointerLikeTypeKind::Reference && *is_const {
                            target.to_cpp_code(None)?
                        } else {
                            bail!("Unsupported original type for QFlagsToUInt conversion");
                        }
                    } else {
                        argument.argument_type.original_type.to_cpp_code(None)?
                    };
                    result = format!("{}({})", type_text, result);
                }
            }
            Ok(result)
        })?;
    Ok(r.join(", "))
}

/// Generates code for the value returned by the FFI method.
#[allow(clippy::collapsible_if)]
fn returned_expression(method: &CppFfiFunction) -> Result<String> {
    let result = if method
        .kind
        .cpp_function()
        .map(|m| m.is_destructor())
        .unwrap_or(false)
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::This)
        {
            format!("c2r_call_destructor({})", arg.name)
        } else {
            unexpected!("no this arg in destructor");
        }
    } else {
        let result_without_args =
            if let Some(cpp_function) = method.kind.cpp_function().filter(|m| m.is_constructor()) {
                match method.allocation_place {
                    ReturnValueAllocationPlace::Stack => {
                        if let Some(arg) = method
                            .arguments
                            .iter()
                            .find(|x| x.meaning == CppFfiArgumentMeaning::ReturnValue)
                        {
                            format!(
                                "new({}) {}",
                                arg.name,
                                cpp_function.class_type().unwrap().to_cpp_code()?
                            )
                        } else {
                            unexpected!("return value argument not found\n{:?}", method);
                        }
                    }
                    ReturnValueAllocationPlace::Heap => {
                        format!("new {}", cpp_function.class_type().unwrap().to_cpp_code()?)
                    }
                    ReturnValueAllocationPlace::NotApplicable => {
                        unexpected!("NotApplicable in constructor");
                    }
                }
            } else {
                let path = match method.kind {
                    CppFfiFunctionKind::Function {
                        ref cpp_function, ..
                    } => &cpp_function.path,
                    CppFfiFunctionKind::FieldAccessor { ref field, .. } => &field.path,
                };

                if let Some(arg) = method
                    .arguments
                    .iter()
                    .find(|x| x.meaning == CppFfiArgumentMeaning::This)
                {
                    format!("{}->{}", arg.name, path.last().to_cpp_code()?)
                } else {
                    path.to_cpp_code()?
                }
            };
        if let CppFfiFunctionKind::FieldAccessor {
            ref accessor_type, ..
        } = method.kind
        {
            if accessor_type == &CppFieldAccessorType::Setter {
                format!("{} = {}", result_without_args, arguments_values(method)?)
            } else {
                result_without_args
            }
        } else {
            format!("{}({})", result_without_args, arguments_values(method)?)
        }
    };
    convert_return_type(method, result)
}

/// Generates body of the FFI method implementation.
fn source_body(method: &CppFfiFunction) -> Result<String> {
    if method
        .kind
        .cpp_function()
        .map(|m| m.is_destructor())
        .unwrap_or(false)
        && method.allocation_place == ReturnValueAllocationPlace::Heap
    {
        if let Some(arg) = method
            .arguments
            .iter()
            .find(|x| x.meaning == CppFfiArgumentMeaning::This)
        {
            Ok(format!("delete {};\n", arg.name))
        } else {
            panic!("Error: no this argument found\n{:?}", method);
        }
    } else {
        Ok(format!(
            "{}{};\n",
            if method.return_type.ffi_type.is_void() {
                ""
            } else {
                "return "
            },
            returned_expression(&method)?
        ))
    }
}

/// Generates implementation of the FFI method for the source file.
pub fn function_implementation(method: &CppFfiFunction) -> Result<String> {
    Ok(format!(
        "C2R_EXPORT {} {{\n  {}}}\n\n",
        function_signature(method)?,
        source_body(&method)?
    ))
}

/// Generates a source file with the specified FFI methods.
pub fn generate_cpp_file(
    data: &[CppDatabaseItem],
    file_path: &Path,
    global_header_name: &str,
) -> Result<()> {
    //    let cpp_path = self
    //      .lib_path
    //      .join("src")
    //      .join(format!("{}_{}.cpp", &self.lib_name, data.name));

    let mut cpp_file = create_file(file_path)?;
    cpp_file.write(format!("#include \"{}\"\n", global_header_name))?;

    let mut any_slot_wrappers = false;
    for item in data {
        for ffi_item in &item.ffi_items {
            match ffi_item.kind {
                CppFfiItemKind::Function(ref cpp_ffi_function) => {
                    // TODO: write less extern C
                    cpp_file.write("extern \"C\" {\n\n")?;
                    cpp_file.write(function_implementation(cpp_ffi_function)?)?;
                    cpp_file.write("\n} // extern \"C\"\n\n")?;
                }
                CppFfiItemKind::QtSlotWrapper(ref qt_slot_wrapper) => {
                    any_slot_wrappers = true;
                    cpp_file.write(self::qt_slot_wrapper(qt_slot_wrapper)?)?;
                }
            }
        }
    }
    if any_slot_wrappers {
        let moc_output = get_command_output(Command::new("moc").arg("-i").arg(file_path))?;
        cpp_file.write(format!(
            "// start of MOC generated code\n{}\n// end of MOC generated code\n",
            moc_output
        ))?;
    }
    Ok(())
}

/// Generates a C++ program that determines sizes of target C++ types
/// on the current platform and outputs the Rust code for `sized_types.rs` module
/// to the standard output.
pub fn generate_cpp_type_size_requester(
    rust_database: &RustDatabase,
    include_directives: &[PathBuf],
    mut output: impl Write,
) -> Result<()> {
    for dir in include_directives {
        writeln!(output, "#include <{}>", path_to_str(dir)?)?;
    }
    writeln!(output, "#include <stdio.h>\n\nint main() {{")?;

    for item in &rust_database.items {
        if let RustItemKind::Struct(ref data) = item.kind {
            if let RustStructKind::SizedType(ref cpp_path) = data.kind {
                let cpp_path_code = cpp_path.to_cpp_code()?;

                writeln!(
                    output,
                    "printf(\"#[repr(C, align(%d))]\\n\", alignof({}));",
                    cpp_path_code
                )?;

                writeln!(
                    output,
                    "printf(\"pub struct {}([u8; %d]);\\n\\n\", sizeof({}));",
                    data.path.last(),
                    cpp_path_code
                )?;
            }
        }
    }

    writeln!(output, "}}")?;
    Ok(())
}
<|endoftext|>


<commit_before>//! CSS font properties.

use cssparser::Parser;

use crate::drawing_ctx::ViewParams;
use crate::error::*;
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontSizeSpec {
    Smaller,
    Larger,
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    Value(Length<Both>),
}

impl FontSizeSpec {
    pub fn value(&self) -> Length<Both> {
        match self {
            FontSizeSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self, v: &ComputedValues) -> Self {
        let compute_points = |p| 12.0 * 1.2f64.powf(p) / POINTS_PER_INCH;

        let parent = v.font_size().0.value();

        // The parent must already have resolved to an absolute unit
        assert!(
            parent.unit != LengthUnit::Percent
                && parent.unit != LengthUnit::Em
                && parent.unit != LengthUnit::Ex
        );

        use FontSizeSpec::*;

        #[rustfmt::skip]
        let new_size = match self {
            Smaller => Length::<Both>::new(parent.length / 1.2,  parent.unit),
            Larger  => Length::<Both>::new(parent.length * 1.2,  parent.unit),
            XXSmall => Length::<Both>::new(compute_points(-3.0), LengthUnit::In),
            XSmall  => Length::<Both>::new(compute_points(-2.0), LengthUnit::In),
            Small   => Length::<Both>::new(compute_points(-1.0), LengthUnit::In),
            Medium  => Length::<Both>::new(compute_points(0.0),  LengthUnit::In),
            Large   => Length::<Both>::new(compute_points(1.0),  LengthUnit::In),
            XLarge  => Length::<Both>::new(compute_points(2.0),  LengthUnit::In),
            XXLarge => Length::<Both>::new(compute_points(3.0),  LengthUnit::In),

            Value(s) if s.unit == LengthUnit::Percent => {
                Length::<Both>::new(parent.length * s.length, parent.unit)
            }

            Value(s) if s.unit == LengthUnit::Em => {
                Length::<Both>::new(parent.length * s.length, parent.unit)
            }

            Value(s) if s.unit == LengthUnit::Ex => {
                // FIXME: it would be nice to know the actual Ex-height
                // of the font.
                Length::<Both>::new(parent.length * s.length / 2.0, parent.unit)
            }

            Value(s) => *s,
        };

        FontSizeSpec::Value(new_size)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for FontSizeSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<FontSizeSpec, ParseError<'i>> {
        parser
            .try_parse(|p| Length::<Both>::parse(p))
            .and_then(|l| Ok(FontSizeSpec::Value(l)))
            .or_else(|_| {
                Ok(parse_identifiers!(
                    parser,
                    "smaller" => FontSizeSpec::Smaller,
                    "larger" => FontSizeSpec::Larger,
                    "xx-small" => FontSizeSpec::XXSmall,
                    "x-small" => FontSizeSpec::XSmall,
                    "small" => FontSizeSpec::Small,
                    "medium" => FontSizeSpec::Medium,
                    "large" => FontSizeSpec::Large,
                    "x-large" => FontSizeSpec::XLarge,
                    "xx-large" => FontSizeSpec::XXLarge,
                )?)
            })
    }
}

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontWeightSpec {
    Normal,
    Bold,
    Bolder,
    Lighter,
    W100,
    W200,
    W300,
    W400,
    W500,
    W600,
    W700,
    W800,
    W900,
}

impl Parse for FontWeightSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<FontWeightSpec, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(parse_identifiers!(
                    p,
                    "normal" => FontWeightSpec::Normal,
                    "bold" => FontWeightSpec::Bold,
                    "bolder" => FontWeightSpec::Bolder,
                    "lighter" => FontWeightSpec::Lighter,
                )?)
            })
            .or_else(|_: ParseError| {
                let loc = parser.current_source_location();
                let i = parser.expect_integer()?;
                match i {
                    100 => Ok(FontWeightSpec::W100),
                    200 => Ok(FontWeightSpec::W200),
                    300 => Ok(FontWeightSpec::W300),
                    400 => Ok(FontWeightSpec::W400),
                    500 => Ok(FontWeightSpec::W500),
                    600 => Ok(FontWeightSpec::W600),
                    700 => Ok(FontWeightSpec::W700),
                    800 => Ok(FontWeightSpec::W800),
                    900 => Ok(FontWeightSpec::W900),
                    _ => Err(loc.new_custom_error(ValueErrorKind::parse_error("parse error"))),
                }
            })
    }
}

// https://www.w3.org/TR/css-text-3/#letter-spacing-property
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LetterSpacingSpec {
    Normal,
    Value(Length<Horizontal>),
}

impl LetterSpacingSpec {
    pub fn value(&self) -> Length<Horizontal> {
        match self {
            LetterSpacingSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self) -> Self {
        let spacing = match self {
            LetterSpacingSpec::Normal => Length::<Horizontal>::new(0.0, LengthUnit::Px),
            LetterSpacingSpec::Value(s) => *s,
        };

        LetterSpacingSpec::Value(spacing)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for LetterSpacingSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<LetterSpacingSpec, ParseError<'i>> {
        parser
            .try_parse(|p| Length::<Horizontal>::parse(p))
            .and_then(|l| Ok(LetterSpacingSpec::Value(l)))
            .or_else(|_| {
                Ok(parse_identifiers!(
                    parser,
                    "normal" => LetterSpacingSpec::Normal,
                )?)
            })
    }
}

/// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-family
#[derive(Debug, Clone, PartialEq)]
pub struct MultiFontFamily(pub String);

impl Parse for MultiFontFamily {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MultiFontFamily, ParseError<'i>> {
        let loc = parser.current_source_location();

        let fonts = parser.parse_comma_separated(|parser| {
            if let Ok(cow) = parser.try_parse(|p| p.expect_string_cloned()) {
                if cow == "" {
                    return Err(loc.new_custom_error(ValueErrorKind::value_error(
                        "empty string is not a valid font family name",
                    )));
                }

                return Ok(cow);
            }

            let first_ident = parser.expect_ident()?.clone();
            let mut value = first_ident.as_ref().to_owned();

            while let Ok(cow) = parser.try_parse(|p| p.expect_ident_cloned()) {
                value.push(' ');
                value.push_str(&cow);
            }
            Ok(cssparser::CowRcStr::from(value))
        })?;

        Ok(MultiFontFamily(fonts.join(",")))
    }
}

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

    use crate::properties::{ParsedProperty, SpecifiedValue, SpecifiedValues};
    use crate::property_defs::FontSize;
    use crate::property_macros::Property;

    #[test]
    fn detects_invalid_invalid_font_size() {
        assert!(FontSizeSpec::parse_str("furlong").is_err());
    }

    #[test]
    fn computes_parent_relative_font_size() {
        let mut specified = SpecifiedValues::default();
        specified.set_parsed_property(&ParsedProperty::FontSize(SpecifiedValue::Specified(
            FontSize::parse_str("10px").unwrap(),
        )));

        let mut values = ComputedValues::default();
        specified.to_computed_values(&mut values);

        assert_eq!(
            FontSize::parse_str("150%").unwrap().compute(&values),
            FontSize::parse_str("15px").unwrap()
        );

        assert_eq!(
            FontSize::parse_str("1.5em").unwrap().compute(&values),
            FontSize::parse_str("15px").unwrap()
        );

        assert_eq!(
            FontSize::parse_str("1ex").unwrap().compute(&values),
            FontSize::parse_str("5px").unwrap()
        );

        let smaller = FontSize::parse_str("smaller").unwrap().compute(&values).0;
        if let FontSizeSpec::Value(v) = smaller {
            assert!(v.length < 10.0);
            assert_eq!(v.unit, LengthUnit::Px);
        } else {
            unreachable!();
        }

        let larger = FontSize::parse_str("larger").unwrap().compute(&values).0;
        if let FontSizeSpec::Value(v) = larger {
            assert!(v.length > 10.0);
            assert_eq!(v.unit, LengthUnit::Px);
        } else {
            unreachable!();
        }
    }

    #[test]
    fn parses_font_weight() {
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("normal"),
            Ok(FontWeightSpec::Normal)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("bold"),
            Ok(FontWeightSpec::Bold)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("100"),
            Ok(FontWeightSpec::W100)
        );
    }

    #[test]
    fn detects_invalid_font_weight() {
        assert!(<FontWeightSpec as Parse>::parse_str("").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("strange").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("314").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("3.14").is_err());
    }

    #[test]
    fn parses_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal"),
            Ok(LetterSpacingSpec::Normal)
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em"),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn computes_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                0.0,
                LengthUnit::Px,
            )))
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn detects_invalid_invalid_letter_spacing() {
        assert!(LetterSpacingSpec::parse_str("furlong").is_err());
    }

    #[test]
    fn parses_font_family() {
        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("'Hello world'"),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("\"Hello world\""),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("  Hello  world  "),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("Plonk"),
            Ok(MultiFontFamily("Plonk".to_owned()))
        );
    }

    #[test]
    fn parses_multiple_font_family() {
        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("serif,monospace,\"Hello world\""),
            Ok(MultiFontFamily("serif,monospace,Hello world".to_owned()))
        );
    }

    #[test]
    fn detects_invalid_font_family() {
        assert!(<MultiFontFamily as Parse>::parse_str("").is_err());
        assert!(<MultiFontFamily as Parse>::parse_str("''").is_err());
        assert!(<MultiFontFamily as Parse>::parse_str("42").is_err());
    }
}
<commit_msg>Ensure that font-family="quoted  with  multiple  spaces'" preserves the spaces<commit_after>//! CSS font properties.

use cssparser::Parser;

use crate::drawing_ctx::ViewParams;
use crate::error::*;
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontSizeSpec {
    Smaller,
    Larger,
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    Value(Length<Both>),
}

impl FontSizeSpec {
    pub fn value(&self) -> Length<Both> {
        match self {
            FontSizeSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self, v: &ComputedValues) -> Self {
        let compute_points = |p| 12.0 * 1.2f64.powf(p) / POINTS_PER_INCH;

        let parent = v.font_size().0.value();

        // The parent must already have resolved to an absolute unit
        assert!(
            parent.unit != LengthUnit::Percent
                && parent.unit != LengthUnit::Em
                && parent.unit != LengthUnit::Ex
        );

        use FontSizeSpec::*;

        #[rustfmt::skip]
        let new_size = match self {
            Smaller => Length::<Both>::new(parent.length / 1.2,  parent.unit),
            Larger  => Length::<Both>::new(parent.length * 1.2,  parent.unit),
            XXSmall => Length::<Both>::new(compute_points(-3.0), LengthUnit::In),
            XSmall  => Length::<Both>::new(compute_points(-2.0), LengthUnit::In),
            Small   => Length::<Both>::new(compute_points(-1.0), LengthUnit::In),
            Medium  => Length::<Both>::new(compute_points(0.0),  LengthUnit::In),
            Large   => Length::<Both>::new(compute_points(1.0),  LengthUnit::In),
            XLarge  => Length::<Both>::new(compute_points(2.0),  LengthUnit::In),
            XXLarge => Length::<Both>::new(compute_points(3.0),  LengthUnit::In),

            Value(s) if s.unit == LengthUnit::Percent => {
                Length::<Both>::new(parent.length * s.length, parent.unit)
            }

            Value(s) if s.unit == LengthUnit::Em => {
                Length::<Both>::new(parent.length * s.length, parent.unit)
            }

            Value(s) if s.unit == LengthUnit::Ex => {
                // FIXME: it would be nice to know the actual Ex-height
                // of the font.
                Length::<Both>::new(parent.length * s.length / 2.0, parent.unit)
            }

            Value(s) => *s,
        };

        FontSizeSpec::Value(new_size)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for FontSizeSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<FontSizeSpec, ParseError<'i>> {
        parser
            .try_parse(|p| Length::<Both>::parse(p))
            .and_then(|l| Ok(FontSizeSpec::Value(l)))
            .or_else(|_| {
                Ok(parse_identifiers!(
                    parser,
                    "smaller" => FontSizeSpec::Smaller,
                    "larger" => FontSizeSpec::Larger,
                    "xx-small" => FontSizeSpec::XXSmall,
                    "x-small" => FontSizeSpec::XSmall,
                    "small" => FontSizeSpec::Small,
                    "medium" => FontSizeSpec::Medium,
                    "large" => FontSizeSpec::Large,
                    "x-large" => FontSizeSpec::XLarge,
                    "xx-large" => FontSizeSpec::XXLarge,
                )?)
            })
    }
}

// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontWeightSpec {
    Normal,
    Bold,
    Bolder,
    Lighter,
    W100,
    W200,
    W300,
    W400,
    W500,
    W600,
    W700,
    W800,
    W900,
}

impl Parse for FontWeightSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<FontWeightSpec, ParseError<'i>> {
        parser
            .try_parse(|p| {
                Ok(parse_identifiers!(
                    p,
                    "normal" => FontWeightSpec::Normal,
                    "bold" => FontWeightSpec::Bold,
                    "bolder" => FontWeightSpec::Bolder,
                    "lighter" => FontWeightSpec::Lighter,
                )?)
            })
            .or_else(|_: ParseError| {
                let loc = parser.current_source_location();
                let i = parser.expect_integer()?;
                match i {
                    100 => Ok(FontWeightSpec::W100),
                    200 => Ok(FontWeightSpec::W200),
                    300 => Ok(FontWeightSpec::W300),
                    400 => Ok(FontWeightSpec::W400),
                    500 => Ok(FontWeightSpec::W500),
                    600 => Ok(FontWeightSpec::W600),
                    700 => Ok(FontWeightSpec::W700),
                    800 => Ok(FontWeightSpec::W800),
                    900 => Ok(FontWeightSpec::W900),
                    _ => Err(loc.new_custom_error(ValueErrorKind::parse_error("parse error"))),
                }
            })
    }
}

// https://www.w3.org/TR/css-text-3/#letter-spacing-property
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LetterSpacingSpec {
    Normal,
    Value(Length<Horizontal>),
}

impl LetterSpacingSpec {
    pub fn value(&self) -> Length<Horizontal> {
        match self {
            LetterSpacingSpec::Value(s) => *s,
            _ => unreachable!(),
        }
    }

    pub fn compute(&self) -> Self {
        let spacing = match self {
            LetterSpacingSpec::Normal => Length::<Horizontal>::new(0.0, LengthUnit::Px),
            LetterSpacingSpec::Value(s) => *s,
        };

        LetterSpacingSpec::Value(spacing)
    }

    pub fn normalize(&self, values: &ComputedValues, params: &ViewParams) -> f64 {
        self.value().normalize(values, params)
    }
}

impl Parse for LetterSpacingSpec {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<LetterSpacingSpec, ParseError<'i>> {
        parser
            .try_parse(|p| Length::<Horizontal>::parse(p))
            .and_then(|l| Ok(LetterSpacingSpec::Value(l)))
            .or_else(|_| {
                Ok(parse_identifiers!(
                    parser,
                    "normal" => LetterSpacingSpec::Normal,
                )?)
            })
    }
}

/// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-family
#[derive(Debug, Clone, PartialEq)]
pub struct MultiFontFamily(pub String);

impl Parse for MultiFontFamily {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<MultiFontFamily, ParseError<'i>> {
        let loc = parser.current_source_location();

        let fonts = parser.parse_comma_separated(|parser| {
            if let Ok(cow) = parser.try_parse(|p| p.expect_string_cloned()) {
                if cow == "" {
                    return Err(loc.new_custom_error(ValueErrorKind::value_error(
                        "empty string is not a valid font family name",
                    )));
                }

                return Ok(cow);
            }

            let first_ident = parser.expect_ident()?.clone();
            let mut value = first_ident.as_ref().to_owned();

            while let Ok(cow) = parser.try_parse(|p| p.expect_ident_cloned()) {
                value.push(' ');
                value.push_str(&cow);
            }
            Ok(cssparser::CowRcStr::from(value))
        })?;

        Ok(MultiFontFamily(fonts.join(",")))
    }
}

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

    use crate::properties::{ParsedProperty, SpecifiedValue, SpecifiedValues};
    use crate::property_defs::FontSize;
    use crate::property_macros::Property;

    #[test]
    fn detects_invalid_invalid_font_size() {
        assert!(FontSizeSpec::parse_str("furlong").is_err());
    }

    #[test]
    fn computes_parent_relative_font_size() {
        let mut specified = SpecifiedValues::default();
        specified.set_parsed_property(&ParsedProperty::FontSize(SpecifiedValue::Specified(
            FontSize::parse_str("10px").unwrap(),
        )));

        let mut values = ComputedValues::default();
        specified.to_computed_values(&mut values);

        assert_eq!(
            FontSize::parse_str("150%").unwrap().compute(&values),
            FontSize::parse_str("15px").unwrap()
        );

        assert_eq!(
            FontSize::parse_str("1.5em").unwrap().compute(&values),
            FontSize::parse_str("15px").unwrap()
        );

        assert_eq!(
            FontSize::parse_str("1ex").unwrap().compute(&values),
            FontSize::parse_str("5px").unwrap()
        );

        let smaller = FontSize::parse_str("smaller").unwrap().compute(&values).0;
        if let FontSizeSpec::Value(v) = smaller {
            assert!(v.length < 10.0);
            assert_eq!(v.unit, LengthUnit::Px);
        } else {
            unreachable!();
        }

        let larger = FontSize::parse_str("larger").unwrap().compute(&values).0;
        if let FontSizeSpec::Value(v) = larger {
            assert!(v.length > 10.0);
            assert_eq!(v.unit, LengthUnit::Px);
        } else {
            unreachable!();
        }
    }

    #[test]
    fn parses_font_weight() {
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("normal"),
            Ok(FontWeightSpec::Normal)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("bold"),
            Ok(FontWeightSpec::Bold)
        );
        assert_eq!(
            <FontWeightSpec as Parse>::parse_str("100"),
            Ok(FontWeightSpec::W100)
        );
    }

    #[test]
    fn detects_invalid_font_weight() {
        assert!(<FontWeightSpec as Parse>::parse_str("").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("strange").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("314").is_err());
        assert!(<FontWeightSpec as Parse>::parse_str("3.14").is_err());
    }

    #[test]
    fn parses_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal"),
            Ok(LetterSpacingSpec::Normal)
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em"),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn computes_letter_spacing() {
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("normal").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                0.0,
                LengthUnit::Px,
            )))
        );
        assert_eq!(
            <LetterSpacingSpec as Parse>::parse_str("10em").map(|s| s.compute()),
            Ok(LetterSpacingSpec::Value(Length::<Horizontal>::new(
                10.0,
                LengthUnit::Em,
            )))
        );
    }

    #[test]
    fn detects_invalid_invalid_letter_spacing() {
        assert!(LetterSpacingSpec::parse_str("furlong").is_err());
    }

    #[test]
    fn parses_font_family() {
        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("'Hello world'"),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("\"Hello world\""),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("\"Hello world  with  spaces\""),
            Ok(MultiFontFamily("Hello world  with  spaces".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("  Hello  world  "),
            Ok(MultiFontFamily("Hello world".to_owned()))
        );

        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("Plonk"),
            Ok(MultiFontFamily("Plonk".to_owned()))
        );
    }

    #[test]
    fn parses_multiple_font_family() {
        assert_eq!(
            <MultiFontFamily as Parse>::parse_str("serif,monospace,\"Hello world\", with  spaces "),
            Ok(MultiFontFamily("serif,monospace,Hello world,with spaces".to_owned()))
        );
    }

    #[test]
    fn detects_invalid_font_family() {
        assert!(<MultiFontFamily as Parse>::parse_str("").is_err());
        assert!(<MultiFontFamily as Parse>::parse_str("''").is_err());
        assert!(<MultiFontFamily as Parse>::parse_str("42").is_err());
    }
}
<|endoftext|>


<commit_before>//! Byte swap intrinsics.

#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

/// Returns an integer with the reversed byte order of x
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_bswap64)
#[inline]
#[cfg_attr(test, assert_instr(bswap))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _bswap64(x: i64) -> i64 {
    bswap_i64(x)
}

#[allow(improper_ctypes)]
extern "C" {
    #[link_name = "llvm.bswap.i64"]
    fn bswap_i64(x: i64) -> i64;
}

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

    #[test]
    fn test_bswap64() {
        unsafe {
            assert_eq!(_bswap64(0x0EADBEEFFADECA0E), 0x0ECADEFAEFBEAD0E);
            assert_eq!(_bswap64(0x0000000000000000), 0x0000000000000000);
        }
    }
}
<commit_msg>Use <i64>::swap_bytes instead of llvm.bswap.i64<commit_after>//! Byte swap intrinsics.

#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

/// Returns an integer with the reversed byte order of x
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_bswap64)
#[inline]
#[cfg_attr(test, assert_instr(bswap))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _bswap64(x: i64) -> i64 {
   x.swap_bytes()
}

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

    #[test]
    fn test_bswap64() {
        unsafe {
            assert_eq!(_bswap64(0x0EADBEEFFADECA0E), 0x0ECADEFAEFBEAD0E);
            assert_eq!(_bswap64(0x0000000000000000), 0x0000000000000000);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Remove unused code in allele_display_name()<commit_after><|endoftext|>


<commit_before>#![warn(rust_2018_idioms, clippy::all)]

use futures::{try_ready, Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "runtime")]
use tokio_postgres::MakeTlsConnect;
use tokio_postgres::{ChannelBinding, TlsConnect};
use tokio_tls::{Connect, TlsStream};

#[cfg(test)]
mod test;

#[cfg(feature = "runtime")]
#[derive(Clone)]
pub struct MakeTlsConnector(tokio_tls::TlsConnector);

#[cfg(feature = "runtime")]
impl MakeTlsConnector {
    pub fn new(connector: native_tls::TlsConnector) -> MakeTlsConnector {
        MakeTlsConnector(tokio_tls::TlsConnector::from(connector))
    }
}

#[cfg(feature = "runtime")]
impl<S> MakeTlsConnect<S> for MakeTlsConnector
where
    S: AsyncRead + AsyncWrite,
{
    type Stream = TlsStream<S>;
    type TlsConnect = TlsConnector;
    type Error = native_tls::Error;

    fn make_tls_connect(&mut self, domain: &str) -> Result<TlsConnector, native_tls::Error> {
        Ok(TlsConnector {
            connector: self.0.clone(),
            domain: domain.to_string(),
        })
    }
}

pub struct TlsConnector {
    connector: tokio_tls::TlsConnector,
    domain: String,
}

impl TlsConnector {
    pub fn new(connector: native_tls::TlsConnector, domain: &str) -> TlsConnector {
        TlsConnector {
            connector: tokio_tls::TlsConnector::from(connector),
            domain: domain.to_string(),
        }
    }
}

impl<S> TlsConnect<S> for TlsConnector
where
    S: AsyncRead + AsyncWrite,
{
    type Stream = TlsStream<S>;
    type Error = native_tls::Error;
    type Future = TlsConnectFuture<S>;

    fn connect(self, stream: S) -> TlsConnectFuture<S> {
        TlsConnectFuture(self.connector.connect(&self.domain, stream))
    }
}

pub struct TlsConnectFuture<S>(Connect<S>);

impl<S> Future for TlsConnectFuture<S>
where
    S: AsyncRead + AsyncWrite,
{
    type Item = (TlsStream<S>, ChannelBinding);
    type Error = native_tls::Error;

    fn poll(&mut self) -> Poll<(TlsStream<S>, ChannelBinding), native_tls::Error> {
        let stream = try_ready!(self.0.poll());

        let channel_binding = match stream.get_ref().tls_server_end_point().unwrap_or(None) {
            Some(buf) => ChannelBinding::tls_server_end_point(buf),
            None => ChannelBinding::none(),
        };

        Ok(Async::Ready((stream, channel_binding)))
    }
}
<commit_msg>Simplify native tls connector<commit_after>#![warn(rust_2018_idioms, clippy::all)]

use futures::{try_ready, Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "runtime")]
use tokio_postgres::MakeTlsConnect;
use tokio_postgres::{ChannelBinding, TlsConnect};
use tokio_tls::{Connect, TlsStream};

#[cfg(test)]
mod test;

#[cfg(feature = "runtime")]
#[derive(Clone)]
pub struct MakeTlsConnector(native_tls::TlsConnector);

#[cfg(feature = "runtime")]
impl MakeTlsConnector {
    pub fn new(connector: native_tls::TlsConnector) -> MakeTlsConnector {
        MakeTlsConnector(connector)
    }
}

#[cfg(feature = "runtime")]
impl<S> MakeTlsConnect<S> for MakeTlsConnector
where
    S: AsyncRead + AsyncWrite,
{
    type Stream = TlsStream<S>;
    type TlsConnect = TlsConnector;
    type Error = native_tls::Error;

    fn make_tls_connect(&mut self, domain: &str) -> Result<TlsConnector, native_tls::Error> {
        Ok(TlsConnector::new(self.0.clone(), domain))
    }
}

pub struct TlsConnector {
    connector: tokio_tls::TlsConnector,
    domain: String,
}

impl TlsConnector {
    pub fn new(connector: native_tls::TlsConnector, domain: &str) -> TlsConnector {
        TlsConnector {
            connector: tokio_tls::TlsConnector::from(connector),
            domain: domain.to_string(),
        }
    }
}

impl<S> TlsConnect<S> for TlsConnector
where
    S: AsyncRead + AsyncWrite,
{
    type Stream = TlsStream<S>;
    type Error = native_tls::Error;
    type Future = TlsConnectFuture<S>;

    fn connect(self, stream: S) -> TlsConnectFuture<S> {
        TlsConnectFuture(self.connector.connect(&self.domain, stream))
    }
}

pub struct TlsConnectFuture<S>(Connect<S>);

impl<S> Future for TlsConnectFuture<S>
where
    S: AsyncRead + AsyncWrite,
{
    type Item = (TlsStream<S>, ChannelBinding);
    type Error = native_tls::Error;

    fn poll(&mut self) -> Poll<(TlsStream<S>, ChannelBinding), native_tls::Error> {
        let stream = try_ready!(self.0.poll());

        let channel_binding = match stream.get_ref().tls_server_end_point().unwrap_or(None) {
            Some(buf) => ChannelBinding::tls_server_end_point(buf),
            None => ChannelBinding::none(),
        };

        Ok(Async::Ready((stream, channel_binding)))
    }
}
<|endoftext|>


<commit_before><commit_msg>Improve error messages.<commit_after><|endoftext|>


<commit_before>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusTCPPI};

#[test]
fn new_tcp_pi() {
    assert!(Modbus::new_tcp_pi("::1", "1502").is_ok());
    // ip: &str, port: i32) -> Result<Modbus>;
}

#[test]
#[ignore]
fn tcp_pi_accept() {
    let mut server = Modbus::new_tcp_pi("::1", "1502").unwrap();
    match server.tcp_pi_listen(1) {
        Ok(mut socket) => assert_eq!(server.tcp_pi_accept(&mut socket).unwrap(), 1),
        _ => panic!("could not listen to socket"),
    }
}

#[test]
fn tcp_pi_listen() {
    let mut server = Modbus::new_tcp_pi("::1", "1502").unwrap();
    let client = Modbus::new_tcp_pi("::1", "1502").unwrap();
    match server.tcp_pi_listen(1) {
        Ok(mut _socket) => assert!(client.connect().is_ok()),
        _ => panic!("could not listen to socket"),
    }
}
<commit_msg>Fix failing travis builds<commit_after>extern crate libmodbus_rs;

use libmodbus_rs::{Modbus, ModbusTCPPI};

#[test]
fn new_tcp_pi() {
    assert!(Modbus::new_tcp_pi("::1", "1502").is_ok());
    // ip: &str, port: i32) -> Result<Modbus>;
}

#[test]
#[ignore]
fn tcp_pi_accept() {
    let mut server = Modbus::new_tcp_pi("::1", "1502").unwrap();
    match server.tcp_pi_listen(1) {
        Ok(mut socket) => assert_eq!(server.tcp_pi_accept(&mut socket).unwrap(), 1),
        _ => panic!("could not listen to socket"),
    }
}

#[test]
#[ignore]
fn tcp_pi_listen() {
    let mut server = Modbus::new_tcp_pi("::1", "1502").unwrap();
    let client = Modbus::new_tcp_pi("::1", "1502").unwrap();
    match server.tcp_pi_listen(1) {
        Ok(mut _socket) => assert!(client.connect().is_ok()),
        _ => panic!("could not listen to socket"),
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

struct WorldMap{
    map: HashSet<(i8, i8)>
}

impl WorldMap{

    fn empty() -> WorldMap {
        return WorldMap::new( HashSet::new() );
    }

    fn new(new_map: HashSet<(i8,i8)>) -> WorldMap {
        return WorldMap { map: new_map };
    }

    fn mark_cell_as_alive(&self, cell_coordinates: (i8, i8)) -> WorldMap {
        let mut new_map = self.map.clone();
        new_map.insert(cell_coordinates);
        let new_world = WorldMap::new(new_map);
        return new_world;
    }

    fn is_cell_alive(&self, cell_coordinates: (i8,i8)) -> bool {
        return self.map.contains(&cell_coordinates);
    }

    fn count_alive_neighbours(&self, cell_coordinates: (i8, i8)) -> usize {
        let mut neighbour_coordinates = HashSet::new();
        let (x, y) = cell_coordinates;
        neighbour_coordinates.insert((x-1, y-1));
        neighbour_coordinates.insert((x-1, y));
        neighbour_coordinates.insert((x-1, y+1));
        neighbour_coordinates.insert((x, y-1));
        neighbour_coordinates.insert((x, y+1));
        neighbour_coordinates.insert((x+1, y-1));
        neighbour_coordinates.insert((x+1, y));
        neighbour_coordinates.insert((x+1, y+1));
        let neighbours_alive = self.map.intersection(&neighbour_coordinates).collect::<Vec<&(i8,i8)>>();
        return neighbours_alive.len();
    }
}

#[test]
fn the_only_alive_cell_should_have_no_alive_neighbours() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let new_world = world.mark_cell_as_alive(coords);
    let alive_neighbours_count = new_world.count_alive_neighbours(coords);
    assert_eq!(0, alive_neighbours_count);
}

#[test]
fn alive_neighbour_on_the_diagonal_should_count() {
    let (x, y) = (0,0);
    let coords = (x,y);
    let world = WorldMap::empty();
    let new_world1 = world.mark_cell_as_alive(coords);
    let new_world2 = new_world1.mark_cell_as_alive((x, y+1));
    let alive_neighbours_count = new_world2.count_alive_neighbours(coords);
    assert_eq!(1, alive_neighbours_count);
}

#[test]
fn is_cell_alive_should_return_true_for_alive_cell() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let new_world = world.mark_cell_as_alive(coords);
    let is_alive = new_world.is_cell_alive(coords);
    assert_eq!(true, is_alive);
}

#[test]
fn is_cell_alive_should_return_false_for_dead_cell() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let is_alive = world.is_cell_alive(coords);
    assert_eq!(false, is_alive);
}
<commit_msg>getting all neighbours for specific cell: extracted out a separate function<commit_after>use std::collections::HashSet;

struct WorldMap{
    map: HashSet<(i8, i8)>
}

fn get_neighbours_coordinates(cell_coordinates: (i8,i8)) -> HashSet<(i8,i8)> {
    let mut neighbour_coordinates = HashSet::new();
    let (x, y) = cell_coordinates;
    neighbour_coordinates.insert((x-1, y-1));
    neighbour_coordinates.insert((x-1, y));
    neighbour_coordinates.insert((x-1, y+1));
    neighbour_coordinates.insert((x, y-1));
    neighbour_coordinates.insert((x, y+1));
    neighbour_coordinates.insert((x+1, y-1));
    neighbour_coordinates.insert((x+1, y));
    neighbour_coordinates.insert((x+1, y+1));
    return neighbour_coordinates;
}

impl WorldMap{

    fn empty() -> WorldMap {
        return WorldMap::new( HashSet::new() );
    }

    fn new(new_map: HashSet<(i8,i8)>) -> WorldMap {
        return WorldMap { map: new_map };
    }

    fn mark_cell_as_alive(&self, cell_coordinates: (i8, i8)) -> WorldMap {
        let mut new_map = self.map.clone();
        new_map.insert(cell_coordinates);
        let new_world = WorldMap::new(new_map);
        return new_world;
    }

    fn is_cell_alive(&self, cell_coordinates: (i8,i8)) -> bool {
        return self.map.contains(&cell_coordinates);
    }

    fn count_alive_neighbours(&self, cell_coordinates: (i8, i8)) -> usize {
        let neighbour_coordinates = get_neighbours_coordinates(cell_coordinates);
        let neighbours_alive = self.map.intersection(&neighbour_coordinates).collect::<Vec<&(i8,i8)>>();
        return neighbours_alive.len();
    }
}

#[test]
fn the_only_alive_cell_should_have_no_alive_neighbours() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let new_world = world.mark_cell_as_alive(coords);
    let alive_neighbours_count = new_world.count_alive_neighbours(coords);
    assert_eq!(0, alive_neighbours_count);
}

#[test]
fn alive_neighbour_on_the_diagonal_should_count() {
    let (x, y) = (0,0);
    let coords = (x,y);
    let world = WorldMap::empty();
    let new_world1 = world.mark_cell_as_alive(coords);
    let new_world2 = new_world1.mark_cell_as_alive((x, y+1));
    let alive_neighbours_count = new_world2.count_alive_neighbours(coords);
    assert_eq!(1, alive_neighbours_count);
}

#[test]
fn is_cell_alive_should_return_true_for_alive_cell() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let new_world = world.mark_cell_as_alive(coords);
    let is_alive = new_world.is_cell_alive(coords);
    assert_eq!(true, is_alive);
}

#[test]
fn is_cell_alive_should_return_false_for_dead_cell() {
    let coords = (0,0);
    let world = WorldMap::empty();
    let is_alive = world.is_cell_alive(coords);
    assert_eq!(false, is_alive);
}
<|endoftext|>


<commit_before>//! Verified at <https://atcoder.jp/contests/abc120/submissions/4468801>.

use std;

pub struct UnionFind {
    nodes: Vec<UnionFindNode>,
}

enum UnionFindNode {
    Root { size: usize },
    Child { parent: std::cell::RefCell<usize> },
}

impl UnionFind {
    /// Create an union find with `size` vertices.
    pub fn new(size: usize) -> Self {
        UnionFind {
            nodes: (0..size)
                .map(|_| UnionFindNode::Root { size: 1 })
                .collect::<Vec<_>>(),
        }
    }

    /// Get the root index and its component size.
    fn root_node(&self, v: usize) -> (usize, usize) {
        match &self.nodes[v] {
            &UnionFindNode::Root { size } => (v, size),
            &UnionFindNode::Child { parent: ref p } => {
                let (u, size) = self.root_node(*p.borrow());
                // Path compression.
                *p.borrow_mut() = u;
                (u, size)
            }
        }
    }

    /// Get the root index.
    pub fn root(&self, v: usize) -> usize {
        self.root_node(v).0
    }

    /// Get the size of component.
    pub fn size(&self, v: usize) -> usize {
        self.root_node(v).1
    }

    /// Determine if two vertices are in the same component.
    pub fn connects(&self, u: usize, v: usize) -> bool {
        self.root(u) == self.root(v)
    }

    /// Merge components by adding an edge between the two vertices.
    pub fn merge(&mut self, u: usize, v: usize) {
        let (u, u_size) = self.root_node(u);
        let (v, v_size) = self.root_node(v);

        if u == v {
            return;
        }

        if u_size < v_size {
            self.merge(v, u);
            return;
        }

        self.nodes[v] = UnionFindNode::Child {
            parent: std::cell::RefCell::new(u),
        };
        self.nodes[u] = UnionFindNode::Root {
            size: u_size + v_size,
        };
    }
}
<commit_msg>refactor: RefCell ではなく Cell を使う<commit_after>//! Verified at <https://atcoder.jp/contests/abc120/submissions/4468801>.

use std;

pub struct UnionFind {
    nodes: Vec<UnionFindNode>,
}

enum UnionFindNode {
    Root { size: usize },
    Child { parent: std::cell::Cell<usize> },
}

impl UnionFind {
    /// Create an union find with `size` vertices.
    pub fn new(size: usize) -> Self {
        UnionFind {
            nodes: (0..size)
                .map(|_| UnionFindNode::Root { size: 1 })
                .collect::<Vec<_>>(),
        }
    }

    /// Get the root index and its component size.
    fn root_node(&self, v: usize) -> (usize, usize) {
        match &self.nodes[v] {
            &UnionFindNode::Root { size } => (v, size),
            &UnionFindNode::Child { parent: ref p } => {
                let (u, size) = self.root_node(p.get());
                // Path compression.
                p.set(u);
                (u, size)
            }
        }
    }

    /// Get the root index.
    pub fn root(&self, v: usize) -> usize {
        self.root_node(v).0
    }

    /// Get the size of component.
    pub fn size(&self, v: usize) -> usize {
        self.root_node(v).1
    }

    /// Determine if two vertices are in the same component.
    pub fn connects(&self, u: usize, v: usize) -> bool {
        self.root(u) == self.root(v)
    }

    /// Merge components by adding an edge between the two vertices.
    pub fn merge(&mut self, u: usize, v: usize) {
        let (u, u_size) = self.root_node(u);
        let (v, v_size) = self.root_node(v);

        if u == v {
            return;
        }

        if u_size < v_size {
            self.merge(v, u);
            return;
        }

        self.nodes[v] = UnionFindNode::Child {
            parent: std::cell::Cell::new(u),
        };
        self.nodes[u] = UnionFindNode::Root {
            size: u_size + v_size,
        };
    }
}
<|endoftext|>


<commit_before>#![feature(custom_derive)]
#![feature(plugin)]
#![plugin(serde_macros)]
#![feature(question_mark)]

extern crate exonum;
extern crate timestamping;
extern crate env_logger;
extern crate clap;
extern crate toml;
extern crate serde;

use std::path::Path;
use std::fs;
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;

use clap::{Arg, App, SubCommand};
use toml::Encoder;
use serde::Serialize;

use exonum::node::{Node, Configuration};
use exonum::events::{NetworkConfiguration, EventsConfiguration};
use exonum::crypto::{gen_keypair_from_seed, Seed, PublicKey, SecretKey};
use exonum::storage::{MemoryDB, LevelDB, LevelDBOptions};

use timestamping::TimestampingBlockchain;

#[derive(Clone, Debug, Serialize, Deserialize)]
struct KeyPair {
    public_key: PublicKey,
    secret_key: SecretKey,
    address: ::std::net::SocketAddr,
}

#[derive(Debug, Serialize, Deserialize)]
struct TestNetConfiguration {
    validators: Vec<KeyPair>,
    round_timeout: u32,
    status_timeout: u32,
    peers_timeout: u32,
    max_incoming_connections: usize,
    max_outgoing_connections: usize,
}

impl TestNetConfiguration {
    fn from_file(path: &Path) -> Result<TestNetConfiguration, Box<Error>> {
        let mut file = File::open(path)?;
        let mut toml = String::new();
        file.read_to_string(&mut toml)?;
        let cfg = toml::decode_str(&toml);

        // match cfg {
        //     Some(value) => Ok(value),
        //     None => Err(format!("Unable to decode toml file: {:?}", path))
        // }

        return Ok(cfg.unwrap());
    }

    fn save_to_file(&self, path: &Path) -> Result<(), Box<Error>> {
        if let Some(dir) = path.parent() {
            fs::create_dir_all(dir).unwrap();
        }

        let mut e = Encoder::new();
        self.serialize(&mut e).unwrap();
        let mut file = File::create(path).unwrap();
        file.write_all(toml::encode_str(&e.toml).as_bytes())?;

        Ok(())
    }

    fn gen(validators_count: u8) -> TestNetConfiguration {
        let mut pairs = Vec::new();
        for i in 0..validators_count {
            let keys = gen_keypair_from_seed(&Seed::from_slice(&vec![i; 32]).unwrap());
            let addr = format!("127.0.0.1:{}", 7000 + i as u32).parse().unwrap();
            let pair = KeyPair {
                public_key: keys.0.clone(),
                secret_key: keys.1.clone(),
                address: addr,
            };
            pairs.push(pair);
        }

        TestNetConfiguration {
            validators: pairs,
            round_timeout: 1000,
            status_timeout: 5000,
            peers_timeout: 10000,
            max_incoming_connections: validators_count as usize,
            max_outgoing_connections: validators_count as usize,
        }
    }

    fn to_node_configuration(&self, idx: usize, known_peers: Vec<::std::net::SocketAddr>) -> Configuration {
        let validator = self.validators[idx].clone();
        let validators: Vec<_> = self.validators
            .iter()
            .map(|v| v.public_key)
            .collect();

        Configuration {
            public_key: validator.public_key,
            secret_key: validator.secret_key,
            round_timeout: self.round_timeout,
            status_timeout: self.status_timeout,
            peers_timeout: self.peers_timeout,
            network: NetworkConfiguration {
                listen_address: validator.address,
                max_incoming_connections: self.max_incoming_connections,
                max_outgoing_connections: self.max_outgoing_connections,
            },
            events: EventsConfiguration::new(),
            peer_discovery: known_peers,
            validators: validators,
        }
    }
}

fn main() {
    ::std::env::set_var("RUST_LOG", "exonum=info");
    env_logger::init().unwrap();

    let app = App::new("TestNet node")
        .version("0.1")
        .author("Aleksey S. <aleksei.sidorov@xdev.re>")
        .about("Test network node")
        .arg(Arg::with_name("CONFIG")
            .short("c")
            .long("config")
            .value_name("CONFIG_PATH")
            .help("Sets a testnet config file")
            .required(true)
            .takes_value(true))
        .subcommand(SubCommand::with_name("generate")
            .about("Generates default configuration file")
            .version("0.1")
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("COUNT")
                .help("Validators count")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("run")
            .about("Run test node with the given validator id")
            .version("0.1")
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("LEVELDB_PATH")
                .short("d")
                .long("leveldb-path")
                .value_name("LEVELDB_PATH")
                .help("Use leveldb database with the given path")
                .takes_value(true))
            .arg(Arg::with_name("PEERS")
                .short("p")
                .long("known-peers")
                .value_name("PEERS")
                .help("Comma separated list of known validator ids")
                .takes_value(true))
            .arg(Arg::with_name("VALIDATOR")
                .help("Sets a validator id")
                .required(true)
                .index(1)));

    let matches = app.get_matches();
    let path = Path::new(matches.value_of("CONFIG").unwrap());
    match matches.subcommand() {
        ("generate", Some(matches)) => {
            let count: u8 = matches.value_of("COUNT").unwrap().parse().unwrap();
            let cfg = TestNetConfiguration::gen(count);
            cfg.save_to_file(&path).unwrap();
            println!("The configuration was successfully written to file {:?}",
                     path);
        }
        ("run", Some(matches)) => {
            let cfg = TestNetConfiguration::from_file(path).unwrap();
            let idx: usize = matches.value_of("VALIDATOR").unwrap().parse().unwrap();
            let peers = match matches.value_of("PEERS") {
                Some(string) => {
                    string.split(" ")
                        .map(|x| -> usize { x.parse().unwrap() })
                        .map(|x| cfg.validators[x].address)
                        .collect()
                }
                None => {
                    cfg.validators
                        .iter()
                        .map(|v| v.address)
                        .collect()
                }
            };
            println!("Known peers is {:#?}", peers);
            let node_cfg = cfg.to_node_configuration(idx, peers);
            let db_path = matches.value_of("LEVELDB_PATH").map(|x| x.to_owned());

            match db_path {
                Some(db_path) => {
                    println!("Using levedb storage with path: {}", db_path);
                    let mut options = LevelDBOptions::new();
                    options.create_if_missing = true;
                    let leveldb = LevelDB::new(&Path::new(&db_path), options).unwrap();

                    let blockchain = TimestampingBlockchain { db: leveldb };
                    Node::with_config(blockchain, node_cfg).run();
                }
                None => {
                    println!("Using memorydb storage");
                    let blockchain = TimestampingBlockchain { db: MemoryDB::new() };
                    Node::with_config(blockchain, node_cfg).run();
                }
            };
        }
        _ => {
            unreachable!("Wrong subcommand");
        }
    }
}
<commit_msg>Polish testnet code<commit_after>#![feature(custom_derive)]
#![feature(plugin)]
#![plugin(serde_macros)]
#![feature(question_mark)]

extern crate exonum;
extern crate timestamping;
extern crate env_logger;
extern crate clap;
extern crate toml;
extern crate serde;

use std::path::Path;
use std::fs;
use std::error::Error;
use std::io::prelude::*;
use std::fs::File;

use clap::{Arg, App, SubCommand};
use toml::Encoder;
use serde::Serialize;

use exonum::node::{Node, Configuration};
use exonum::events::{NetworkConfiguration, EventsConfiguration};
use exonum::crypto::{gen_keypair_from_seed, Seed, PublicKey, SecretKey};
use exonum::storage::{MemoryDB, LevelDB, LevelDBOptions};

use timestamping::TimestampingBlockchain;

#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestnetValidator {
    public_key: PublicKey,
    secret_key: SecretKey,
    address: ::std::net::SocketAddr,
}

#[derive(Debug, Serialize, Deserialize)]
struct TestnetConfiguration {
    validators: Vec<TestnetValidator>,
    round_timeout: u32,
    status_timeout: u32,
    peers_timeout: u32,
    max_incoming_connections: usize,
    max_outgoing_connections: usize,
}

impl TestnetConfiguration {
    fn from_file(path: &Path) -> Result<TestnetConfiguration, Box<Error>> {
        let mut file = File::open(path)?;
        let mut toml = String::new();
        file.read_to_string(&mut toml)?;
        let cfg = toml::decode_str(&toml);
        return Ok(cfg.unwrap());
    }

    fn save_to_file(&self, path: &Path) -> Result<(), Box<Error>> {
        if let Some(dir) = path.parent() {
            fs::create_dir_all(dir).unwrap();
        }

        let mut e = Encoder::new();
        self.serialize(&mut e).unwrap();
        let mut file = File::create(path).unwrap();
        file.write_all(toml::encode_str(&e.toml).as_bytes())?;

        Ok(())
    }

    fn gen(validators_count: u8) -> TestnetConfiguration {
        let mut pairs = Vec::new();
        for i in 0..validators_count {
            let keys = gen_keypair_from_seed(&Seed::from_slice(&vec![i; 32]).unwrap());
            let addr = format!("127.0.0.1:{}", 7000 + i as u32).parse().unwrap();
            let pair = TestnetValidator {
                public_key: keys.0.clone(),
                secret_key: keys.1.clone(),
                address: addr,
            };
            pairs.push(pair);
        }

        TestnetConfiguration {
            validators: pairs,
            round_timeout: 1000,
            status_timeout: 5000,
            peers_timeout: 10000,
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
        }
    }

    fn to_node_configuration(&self,
                             idx: usize,
                             known_peers: Vec<::std::net::SocketAddr>)
                             -> Configuration {
        let validator = self.validators[idx].clone();
        let validators: Vec<_> = self.validators
            .iter()
            .map(|v| v.public_key)
            .collect();

        Configuration {
            public_key: validator.public_key,
            secret_key: validator.secret_key,
            round_timeout: self.round_timeout,
            status_timeout: self.status_timeout,
            peers_timeout: self.peers_timeout,
            network: NetworkConfiguration {
                listen_address: validator.address,
                max_incoming_connections: self.max_incoming_connections,
                max_outgoing_connections: self.max_outgoing_connections,
            },
            events: EventsConfiguration::new(),
            peer_discovery: known_peers,
            validators: validators,
        }
    }
}

fn main() {
    ::std::env::set_var("RUST_LOG", "exonum=info");
    env_logger::init().unwrap();

    let app = App::new("Testnet node")
        .version("0.1")
        .author("Aleksey S. <aleksei.sidorov@xdev.re>")
        .about("Test network node")
        .arg(Arg::with_name("CONFIG")
            .short("c")
            .long("config")
            .value_name("CONFIG_PATH")
            .help("Sets a testnet config file")
            .required(true)
            .takes_value(true))
        .subcommand(SubCommand::with_name("generate")
            .about("Generates default configuration file")
            .version("0.1")
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("COUNT")
                .help("Validators count")
                .required(true)
                .index(1)))
        .subcommand(SubCommand::with_name("run")
            .about("Run test node with the given validator id")
            .version("0.1")
            .author("Aleksey S. <aleksei.sidorov@xdev.re>")
            .arg(Arg::with_name("LEVELDB_PATH")
                .short("d")
                .long("leveldb-path")
                .value_name("LEVELDB_PATH")
                .help("Use leveldb database with the given path")
                .takes_value(true))
            .arg(Arg::with_name("PEERS")
                .short("p")
                .long("known-peers")
                .value_name("PEERS")
                .help("Comma separated list of known validator ids")
                .takes_value(true))
            .arg(Arg::with_name("VALIDATOR")
                .help("Sets a validator id")
                .required(true)
                .index(1)));

    let matches = app.get_matches();
    let path = Path::new(matches.value_of("CONFIG").unwrap());
    match matches.subcommand() {
        ("generate", Some(matches)) => {
            let count: u8 = matches.value_of("COUNT").unwrap().parse().unwrap();
            let cfg = TestnetConfiguration::gen(count);
            cfg.save_to_file(&path).unwrap();
            println!("The configuration was successfully written to file {:?}",
                     path);
        }
        ("run", Some(matches)) => {
            let cfg = TestnetConfiguration::from_file(path).unwrap();
            let idx: usize = matches.value_of("VALIDATOR").unwrap().parse().unwrap();
            let peers = match matches.value_of("PEERS") {
                Some(string) => {
                    string.split(" ")
                        .map(|x| -> usize { x.parse().unwrap() })
                        .map(|x| cfg.validators[x].address)
                        .collect()
                }
                None => {
                    cfg.validators
                        .iter()
                        .map(|v| v.address)
                        .collect()
                }
            };
            println!("Known peers is {:#?}", peers);
            let node_cfg = cfg.to_node_configuration(idx, peers);
            match matches.value_of("LEVELDB_PATH") {
                Some(ref db_path) => {
                    println!("Using levedb storage with path: {}", db_path);
                    let mut options = LevelDBOptions::new();
                    options.create_if_missing = true;
                    let leveldb = LevelDB::new(&Path::new(db_path), options).unwrap();

                    let blockchain = TimestampingBlockchain { db: leveldb };
                    Node::with_config(blockchain, node_cfg).run();
                }
                None => {
                    println!("Using memorydb storage");
                    let blockchain = TimestampingBlockchain { db: MemoryDB::new() };
                    Node::with_config(blockchain, node_cfg).run();
                }
            };
        }
        _ => {
            unreachable!("Wrong subcommand");
        }
    }
}
<|endoftext|>