<commit_before>#![deny(intra_doc_link_resolution_failure)]

extern crate proc_macro;
extern crate protobuf;

use std::collections::hash_map::HashMap;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};

use protobuf::descriptor::*;
use protobuf::Message;

mod amend_io_error_util;
pub mod case_convert;
mod compiler_plugin;
mod customize;
mod enums;
mod extensions;
mod field;
mod file;
pub(crate) mod file_and_mod;
mod inside;
mod map;
mod message;
mod oneof;
mod protobuf_abs_path;
mod protobuf_ident;
mod protobuf_rel_path;
mod rust_name;
mod rust_types_values;
mod serde;
mod well_known_types;

pub(crate) mod rust;
pub(crate) mod scope;
pub(crate) mod strx;
pub(crate) mod syntax;

use customize::customize_from_rustproto_for_file;
pub use customize::Customize;

pub mod code_writer;

use self::code_writer::CodeWriter;
use self::enums::*;
use self::extensions::*;
use self::message::*;
#[doc(hidden)]
pub use amend_io_error_util::amend_io_error;
use scope::FileScope;
use scope::RootScope;

use crate::file::proto_path_to_fn_file_descriptor;
use inside::protobuf_crate_path;
pub use protobuf_abs_path::ProtobufAbsolutePath;
pub use protobuf_ident::ProtobufIdent;
pub use protobuf_rel_path::ProtobufRelativePath;

use crate::rust::EXPR_VEC_NEW;
use crate::scope::WithScope;
use crate::well_known_types::gen_well_known_types_mod;
#[doc(hidden)]
pub use file::proto_name_to_rs;
use protobuf::reflect::FileDescriptor;

fn escape_byte(s: &mut String, b: u8) {
    if b == b'\n' {
        write!(s, "\\n").unwrap();
    } else if b == b'\r' {
        write!(s, "\\r").unwrap();
    } else if b == b'\t' {
        write!(s, "\\t").unwrap();
    } else if b == b'\\' || b == b'"' {
        write!(s, "\\{}", b as char).unwrap();
    } else if b == b'\0' {
        write!(s, "\\0").unwrap();
    // ASCII printable except space
    } else if b > 0x20 && b < 0x7f {
        write!(s, "{}", b as char).unwrap();
    } else {
        write!(s, "\\x{:02x}", b).unwrap();
    }
}

fn write_file_descriptor(
    file_descriptor: &FileDescriptor,
    customize: &Customize,
    w: &mut CodeWriter,
) {
    w.write_line("/// `FileDescriptor` object which allows dynamic access to files");
    w.pub_fn(
        &format!(
            "file_descriptor() -> {}::reflect::FileDescriptor",
            protobuf_crate_path(customize)
        ),
        |w| {
            w.lazy_static(
                "file_descriptor_lazy",
                &format!(
                    "{}::reflect::GeneratedFileDescriptor",
                    protobuf_crate_path(customize)
                ),
                &format!("{}", protobuf_crate_path(customize)),
            );
            w.block(
                "let file_descriptor = file_descriptor_lazy.get(|| {",
                "});",
                |w| {
                    w.write_line(&format!("let mut deps = {};", EXPR_VEC_NEW));
                    for f in &file_descriptor.proto().dependency {
                        w.write_line(&format!(
                            "deps.push({}());",
                            proto_path_to_fn_file_descriptor(f, customize)
                        ));
                    }

                    let scope = FileScope { file_descriptor };

                    w.write_line(&format!("let mut messages = {};", EXPR_VEC_NEW));
                    for m in scope.find_messages_except_map() {
                        if m.is_map() {
                            continue;
                        }
                        w.write_line(&format!(
                            "messages.push({}::generated_message_descriptor_data());",
                            m.rust_name_to_file(),
                        ));
                    }

                    w.write_line(&format!("let mut enums = {};", EXPR_VEC_NEW));
                    for e in scope.find_enums() {
                        w.write_line(&format!(
                            "enums.push({}::generated_enum_descriptor_data());",
                            e.rust_name_to_file(),
                        ));
                    }

                    w.write_line(&format!(
                        "{}::reflect::GeneratedFileDescriptor::new_generated(",
                        protobuf_crate_path(&customize),
                    ));
                    w.indented(|w| {
                        w.write_line(&format!("file_descriptor_proto(),"));
                        w.write_line(&format!("deps,"));
                        w.write_line(&format!("messages,"));
                        w.write_line(&format!("enums,"));
                    });
                    w.write_line(")");
                },
            );
            w.write_line(&format!(
                "{}::reflect::FileDescriptor::new_generated_2(file_descriptor)",
                protobuf_crate_path(&customize),
            ));
        },
    );
}

fn write_file_descriptor_data(file: &FileDescriptor, customize: &Customize, w: &mut CodeWriter) {
    let fdp_bytes = file.proto().write_to_bytes().unwrap();
    w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
    w.indented(|w| {
        const MAX_LINE_LEN: usize = 72;

        let mut s = String::new();
        for &b in &fdp_bytes {
            let prev_len = s.len();
            escape_byte(&mut s, b);
            let truncate = s.len() > MAX_LINE_LEN;
            if truncate {
                s.truncate(prev_len);
            }
            if truncate || s.len() == MAX_LINE_LEN {
                write!(s, "\\").unwrap();
                w.write_line(&s);
                s.clear();
            }
            if truncate {
                escape_byte(&mut s, b);
            }
        }
        if !s.is_empty() {
            write!(s, "\\").unwrap();
            w.write_line(&s);
            s.clear();
        }
    });
    w.write_line("\";");
    w.write_line("");
    w.write_line("/// `FileDescriptorProto` object which was a source for this generated file");
    w.pub_fn(
        &format!(
            "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
            protobuf_crate_path(customize)
        ),
        |w| {
            w.lazy_static(
                "file_descriptor_proto_lazy",
                &format!(
                    "{}::descriptor::FileDescriptorProto",
                    protobuf_crate_path(customize)
                ),
                &format!("{}", protobuf_crate_path(customize)),
            );
            w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
                w.write_line(&format!(
                    "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
                    protobuf_crate_path(customize)
                ));
            });
        },
    );
    w.write_line("");
    write_file_descriptor(file, &customize, w);
}

pub(crate) struct FileIndex {
    messsage_to_index: HashMap<ProtobufRelativePath, u32>,
    enum_to_index: HashMap<ProtobufRelativePath, u32>,
}

impl FileIndex {
    fn index(file_scope: &FileScope) -> FileIndex {
        FileIndex {
            messsage_to_index: file_scope
                .find_messages()
                .into_iter()
                .map(|m| m.protobuf_name_to_package())
                .enumerate()
                .map(|(i, n)| (n, i as u32))
                .collect(),
            enum_to_index: file_scope
                .find_enums()
                .into_iter()
                .map(|m| m.protobuf_name_to_package())
                .enumerate()
                .map(|(i, n)| (n, i as u32))
                .collect(),
        }
    }
}

fn gen_file(
    file_descriptor: &FileDescriptor,
    _files_map: &HashMap<&Path, &FileDescriptor>,
    root_scope: &RootScope,
    customize: &Customize,
    parser: &str,
) -> Option<compiler_plugin::GenResult> {
    // TODO: use it
    let mut customize = customize.clone();
    // options specified in invocation have precedence over options specified in file
    customize.update_with(&customize_from_rustproto_for_file(
        file_descriptor.proto().options.get_or_default(),
    ));

    let file_scope = FileScope { file_descriptor };
    let scope = file_scope.to_scope();
    let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
        file_descriptor
            .proto()
            .options
            .get_or_default()
            .get_optimize_for()
            == file_options::OptimizeMode::LITE_RUNTIME
    });

    let file_index = FileIndex::index(&file_scope);

    let mut v = Vec::new();

    {
        let mut w = CodeWriter::new(&mut v);

        w.write_generated_by("rust-protobuf", env!("CARGO_PKG_VERSION"), parser);

        w.write_line("");
        w.write_line(&format!(
            "//! Generated file from `{}`",
            file_descriptor.proto().get_name()
        ));
        if customize.inside_protobuf != Some(true) {
            w.write_line("");
            w.write_line("/// Generated files are compatible only with the same version");
            w.write_line("/// of protobuf runtime.");
            w.write_line(&format!(
                "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
                protobuf_crate_path(&customize),
                protobuf::VERSION_IDENT
            ));
        }

        static NESTED_TYPE_NUMBER: protobuf::rt::LazyV2<i32> = protobuf::rt::LazyV2::INIT;
        let message_type_number = *NESTED_TYPE_NUMBER.get(|| {
            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
                .get_field_by_name("message_type")
                .expect("`message_type` must exist")
                .get_proto()
                .get_number()
        });

        let mut path = vec![message_type_number, 0];
        for (id, message) in scope.get_messages().iter().enumerate() {
            // ignore map entries, because they are not used in map fields
            if !message.is_map() {
                path[1] = id as i32;

                w.write_line("");
                MessageGen::new(
                    file_descriptor,
                    message,
                    &file_index,
                    &root_scope,
                    &customize,
                    &path,
                    file_descriptor.proto().source_code_info.as_ref(),
                )
                .write(&mut w);
            }
        }

        static ENUM_TYPE_NUMBER: protobuf::rt::LazyV2<i32> = protobuf::rt::LazyV2::INIT;
        let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
                .get_field_by_name("enum_type")
                .expect("`enum_type` must exist")
                .get_proto()
                .get_number()
        });

        let mut path = vec![enum_type_number, 0];
        for (id, enum_type) in scope.get_enums().iter().enumerate() {
            path[1] = id as i32;

            w.write_line("");
            EnumGen::new(
                enum_type,
                &file_index,
                &customize,
                root_scope,
                &path,
                file_descriptor.proto().source_code_info.as_ref(),
            )
            .write(&mut w);
        }

        write_extensions(file_descriptor, &root_scope, &mut w, &customize);

        if !lite_runtime {
            w.write_line("");
            write_file_descriptor_data(file_descriptor, &customize, &mut w);
        }
    }

    Some(compiler_plugin::GenResult {
        name: proto_name_to_rs(file_descriptor.proto().get_name()),
        content: v,
    })
}

// This function is also used externally by cargo plugin
// https://github.com/plietar/rust-protobuf-build
// So be careful changing its signature.
pub fn gen(
    file_descriptors: &[FileDescriptorProto],
    parser: &str,
    files_to_generate: &[PathBuf],
    customize: &Customize,
) -> Vec<compiler_plugin::GenResult> {
    let file_descriptors = FileDescriptor::new_dynamic_fds(file_descriptors.to_vec());

    let root_scope = RootScope {
        file_descriptors: &file_descriptors,
    };

    let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
    let files_map: HashMap<&Path, &FileDescriptor> = file_descriptors
        .iter()
        .map(|f| (Path::new(f.proto().get_name()), f))
        .collect();

    for file_name in files_to_generate {
        let file = files_map.get(file_name.as_path()).expect(&format!(
            "file not found in file descriptors: {:?}, files: {:?}",
            file_name,
            files_map.keys()
        ));
        results.extend(gen_file(file, &files_map, &root_scope, customize, parser));
    }

    if customize.inside_protobuf.unwrap_or(false) {
        results.push(gen_well_known_types_mod(&file_descriptors));
    }

    results
}

pub fn gen_and_write(
    file_descriptors: &[FileDescriptorProto],
    parser: &str,
    files_to_generate: &[PathBuf],
    out_dir: &Path,
    customize: &Customize,
) -> io::Result<()> {
    match out_dir.metadata() {
        Ok(m) => {
            if !m.is_dir() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("{} is not a directory", out_dir.display()),
                ));
            }
        }
        Err(e) => {
            return Err(amend_io_error(
                e,
                format!("{} does not exist or not accessible", out_dir.display()),
            ));
        }
    }

    let results = gen(file_descriptors, parser, files_to_generate, customize);

    for r in &results {
        let mut file_path = out_dir.to_owned();
        file_path.push(&r.name);
        let mut file_writer = File::create(&file_path)
            .map_err(|e| amend_io_error(e, format!("failed to create {:?}", file_path)))?;
        file_writer
            .write_all(&r.content)
            .map_err(|e| amend_io_error(e, format!("failed to write to {:?}", file_path)))?;
        file_writer
            .flush()
            .map_err(|e| amend_io_error(e, format!("failed to flush {:?}", file_path)))?;
    }

    Ok(())
}

pub fn protoc_gen_rust_main() {
    compiler_plugin::plugin_main(|r| {
        let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
        gen(
            r.file_descriptors,
            "protoc --rust-out=...",
            r.files_to_generate,
            &customize,
        )
    });
}
<commit_msg>Unnecessary Option wrap in codegen<commit_after>#![deny(intra_doc_link_resolution_failure)]

extern crate proc_macro;
extern crate protobuf;

use std::collections::hash_map::HashMap;
use std::fmt::Write as FmtWrite;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};

use protobuf::descriptor::*;
use protobuf::Message;

mod amend_io_error_util;
pub mod case_convert;
mod compiler_plugin;
mod customize;
mod enums;
mod extensions;
mod field;
mod file;
pub(crate) mod file_and_mod;
mod inside;
mod map;
mod message;
mod oneof;
mod protobuf_abs_path;
mod protobuf_ident;
mod protobuf_rel_path;
mod rust_name;
mod rust_types_values;
mod serde;
mod well_known_types;

pub(crate) mod rust;
pub(crate) mod scope;
pub(crate) mod strx;
pub(crate) mod syntax;

use customize::customize_from_rustproto_for_file;
pub use customize::Customize;

pub mod code_writer;

use self::code_writer::CodeWriter;
use self::enums::*;
use self::extensions::*;
use self::message::*;
#[doc(hidden)]
pub use amend_io_error_util::amend_io_error;
use scope::FileScope;
use scope::RootScope;

use crate::file::proto_path_to_fn_file_descriptor;
use inside::protobuf_crate_path;
pub use protobuf_abs_path::ProtobufAbsolutePath;
pub use protobuf_ident::ProtobufIdent;
pub use protobuf_rel_path::ProtobufRelativePath;

use crate::rust::EXPR_VEC_NEW;
use crate::scope::WithScope;
use crate::well_known_types::gen_well_known_types_mod;
#[doc(hidden)]
pub use file::proto_name_to_rs;
use protobuf::reflect::FileDescriptor;

fn escape_byte(s: &mut String, b: u8) {
    if b == b'\n' {
        write!(s, "\\n").unwrap();
    } else if b == b'\r' {
        write!(s, "\\r").unwrap();
    } else if b == b'\t' {
        write!(s, "\\t").unwrap();
    } else if b == b'\\' || b == b'"' {
        write!(s, "\\{}", b as char).unwrap();
    } else if b == b'\0' {
        write!(s, "\\0").unwrap();
    // ASCII printable except space
    } else if b > 0x20 && b < 0x7f {
        write!(s, "{}", b as char).unwrap();
    } else {
        write!(s, "\\x{:02x}", b).unwrap();
    }
}

fn write_file_descriptor(
    file_descriptor: &FileDescriptor,
    customize: &Customize,
    w: &mut CodeWriter,
) {
    w.write_line("/// `FileDescriptor` object which allows dynamic access to files");
    w.pub_fn(
        &format!(
            "file_descriptor() -> {}::reflect::FileDescriptor",
            protobuf_crate_path(customize)
        ),
        |w| {
            w.lazy_static(
                "file_descriptor_lazy",
                &format!(
                    "{}::reflect::GeneratedFileDescriptor",
                    protobuf_crate_path(customize)
                ),
                &format!("{}", protobuf_crate_path(customize)),
            );
            w.block(
                "let file_descriptor = file_descriptor_lazy.get(|| {",
                "});",
                |w| {
                    w.write_line(&format!("let mut deps = {};", EXPR_VEC_NEW));
                    for f in &file_descriptor.proto().dependency {
                        w.write_line(&format!(
                            "deps.push({}());",
                            proto_path_to_fn_file_descriptor(f, customize)
                        ));
                    }

                    let scope = FileScope { file_descriptor };

                    w.write_line(&format!("let mut messages = {};", EXPR_VEC_NEW));
                    for m in scope.find_messages_except_map() {
                        if m.is_map() {
                            continue;
                        }
                        w.write_line(&format!(
                            "messages.push({}::generated_message_descriptor_data());",
                            m.rust_name_to_file(),
                        ));
                    }

                    w.write_line(&format!("let mut enums = {};", EXPR_VEC_NEW));
                    for e in scope.find_enums() {
                        w.write_line(&format!(
                            "enums.push({}::generated_enum_descriptor_data());",
                            e.rust_name_to_file(),
                        ));
                    }

                    w.write_line(&format!(
                        "{}::reflect::GeneratedFileDescriptor::new_generated(",
                        protobuf_crate_path(&customize),
                    ));
                    w.indented(|w| {
                        w.write_line(&format!("file_descriptor_proto(),"));
                        w.write_line(&format!("deps,"));
                        w.write_line(&format!("messages,"));
                        w.write_line(&format!("enums,"));
                    });
                    w.write_line(")");
                },
            );
            w.write_line(&format!(
                "{}::reflect::FileDescriptor::new_generated_2(file_descriptor)",
                protobuf_crate_path(&customize),
            ));
        },
    );
}

fn write_file_descriptor_data(file: &FileDescriptor, customize: &Customize, w: &mut CodeWriter) {
    let fdp_bytes = file.proto().write_to_bytes().unwrap();
    w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
    w.indented(|w| {
        const MAX_LINE_LEN: usize = 72;

        let mut s = String::new();
        for &b in &fdp_bytes {
            let prev_len = s.len();
            escape_byte(&mut s, b);
            let truncate = s.len() > MAX_LINE_LEN;
            if truncate {
                s.truncate(prev_len);
            }
            if truncate || s.len() == MAX_LINE_LEN {
                write!(s, "\\").unwrap();
                w.write_line(&s);
                s.clear();
            }
            if truncate {
                escape_byte(&mut s, b);
            }
        }
        if !s.is_empty() {
            write!(s, "\\").unwrap();
            w.write_line(&s);
            s.clear();
        }
    });
    w.write_line("\";");
    w.write_line("");
    w.write_line("/// `FileDescriptorProto` object which was a source for this generated file");
    w.pub_fn(
        &format!(
            "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
            protobuf_crate_path(customize)
        ),
        |w| {
            w.lazy_static(
                "file_descriptor_proto_lazy",
                &format!(
                    "{}::descriptor::FileDescriptorProto",
                    protobuf_crate_path(customize)
                ),
                &format!("{}", protobuf_crate_path(customize)),
            );
            w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
                w.write_line(&format!(
                    "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
                    protobuf_crate_path(customize)
                ));
            });
        },
    );
    w.write_line("");
    write_file_descriptor(file, &customize, w);
}

pub(crate) struct FileIndex {
    messsage_to_index: HashMap<ProtobufRelativePath, u32>,
    enum_to_index: HashMap<ProtobufRelativePath, u32>,
}

impl FileIndex {
    fn index(file_scope: &FileScope) -> FileIndex {
        FileIndex {
            messsage_to_index: file_scope
                .find_messages()
                .into_iter()
                .map(|m| m.protobuf_name_to_package())
                .enumerate()
                .map(|(i, n)| (n, i as u32))
                .collect(),
            enum_to_index: file_scope
                .find_enums()
                .into_iter()
                .map(|m| m.protobuf_name_to_package())
                .enumerate()
                .map(|(i, n)| (n, i as u32))
                .collect(),
        }
    }
}

fn gen_file(
    file_descriptor: &FileDescriptor,
    _files_map: &HashMap<&Path, &FileDescriptor>,
    root_scope: &RootScope,
    customize: &Customize,
    parser: &str,
) -> compiler_plugin::GenResult {
    // TODO: use it
    let mut customize = customize.clone();
    // options specified in invocation have precedence over options specified in file
    customize.update_with(&customize_from_rustproto_for_file(
        file_descriptor.proto().options.get_or_default(),
    ));

    let file_scope = FileScope { file_descriptor };
    let scope = file_scope.to_scope();
    let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
        file_descriptor
            .proto()
            .options
            .get_or_default()
            .get_optimize_for()
            == file_options::OptimizeMode::LITE_RUNTIME
    });

    let file_index = FileIndex::index(&file_scope);

    let mut v = Vec::new();

    {
        let mut w = CodeWriter::new(&mut v);

        w.write_generated_by("rust-protobuf", env!("CARGO_PKG_VERSION"), parser);

        w.write_line("");
        w.write_line(&format!(
            "//! Generated file from `{}`",
            file_descriptor.proto().get_name()
        ));
        if customize.inside_protobuf != Some(true) {
            w.write_line("");
            w.write_line("/// Generated files are compatible only with the same version");
            w.write_line("/// of protobuf runtime.");
            w.write_line(&format!(
                "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
                protobuf_crate_path(&customize),
                protobuf::VERSION_IDENT
            ));
        }

        static NESTED_TYPE_NUMBER: protobuf::rt::LazyV2<i32> = protobuf::rt::LazyV2::INIT;
        let message_type_number = *NESTED_TYPE_NUMBER.get(|| {
            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
                .get_field_by_name("message_type")
                .expect("`message_type` must exist")
                .get_proto()
                .get_number()
        });

        let mut path = vec![message_type_number, 0];
        for (id, message) in scope.get_messages().iter().enumerate() {
            // ignore map entries, because they are not used in map fields
            if !message.is_map() {
                path[1] = id as i32;

                w.write_line("");
                MessageGen::new(
                    file_descriptor,
                    message,
                    &file_index,
                    &root_scope,
                    &customize,
                    &path,
                    file_descriptor.proto().source_code_info.as_ref(),
                )
                .write(&mut w);
            }
        }

        static ENUM_TYPE_NUMBER: protobuf::rt::LazyV2<i32> = protobuf::rt::LazyV2::INIT;
        let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
                .get_field_by_name("enum_type")
                .expect("`enum_type` must exist")
                .get_proto()
                .get_number()
        });

        let mut path = vec![enum_type_number, 0];
        for (id, enum_type) in scope.get_enums().iter().enumerate() {
            path[1] = id as i32;

            w.write_line("");
            EnumGen::new(
                enum_type,
                &file_index,
                &customize,
                root_scope,
                &path,
                file_descriptor.proto().source_code_info.as_ref(),
            )
            .write(&mut w);
        }

        write_extensions(file_descriptor, &root_scope, &mut w, &customize);

        if !lite_runtime {
            w.write_line("");
            write_file_descriptor_data(file_descriptor, &customize, &mut w);
        }
    }

    compiler_plugin::GenResult {
        name: proto_name_to_rs(file_descriptor.proto().get_name()),
        content: v,
    }
}

// This function is also used externally by cargo plugin
// https://github.com/plietar/rust-protobuf-build
// So be careful changing its signature.
pub fn gen(
    file_descriptors: &[FileDescriptorProto],
    parser: &str,
    files_to_generate: &[PathBuf],
    customize: &Customize,
) -> Vec<compiler_plugin::GenResult> {
    let file_descriptors = FileDescriptor::new_dynamic_fds(file_descriptors.to_vec());

    let root_scope = RootScope {
        file_descriptors: &file_descriptors,
    };

    let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
    let files_map: HashMap<&Path, &FileDescriptor> = file_descriptors
        .iter()
        .map(|f| (Path::new(f.proto().get_name()), f))
        .collect();

    for file_name in files_to_generate {
        let file = files_map.get(file_name.as_path()).expect(&format!(
            "file not found in file descriptors: {:?}, files: {:?}",
            file_name,
            files_map.keys()
        ));
        results.push(gen_file(file, &files_map, &root_scope, customize, parser));
    }

    if customize.inside_protobuf.unwrap_or(false) {
        results.push(gen_well_known_types_mod(&file_descriptors));
    }

    results
}

pub fn gen_and_write(
    file_descriptors: &[FileDescriptorProto],
    parser: &str,
    files_to_generate: &[PathBuf],
    out_dir: &Path,
    customize: &Customize,
) -> io::Result<()> {
    match out_dir.metadata() {
        Ok(m) => {
            if !m.is_dir() {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("{} is not a directory", out_dir.display()),
                ));
            }
        }
        Err(e) => {
            return Err(amend_io_error(
                e,
                format!("{} does not exist or not accessible", out_dir.display()),
            ));
        }
    }

    let results = gen(file_descriptors, parser, files_to_generate, customize);

    for r in &results {
        let mut file_path = out_dir.to_owned();
        file_path.push(&r.name);
        let mut file_writer = File::create(&file_path)
            .map_err(|e| amend_io_error(e, format!("failed to create {:?}", file_path)))?;
        file_writer
            .write_all(&r.content)
            .map_err(|e| amend_io_error(e, format!("failed to write to {:?}", file_path)))?;
        file_writer
            .flush()
            .map_err(|e| amend_io_error(e, format!("failed to flush {:?}", file_path)))?;
    }

    Ok(())
}

pub fn protoc_gen_rust_main() {
    compiler_plugin::plugin_main(|r| {
        let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
        gen(
            r.file_descriptors,
            "protoc --rust-out=...",
            r.files_to_generate,
            &customize,
        )
    });
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::default::Default;
use std::io::stdout;
use std::io::Write;

use toml::Value;

use libimagentrylist::lister::Lister;
use libimagentrylist::result::Result;
use libimagerror::trace::trace_error;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagerror::into::IntoError;
use libimagentrylist::error::ListErrorKind as LEK;

use reference::Ref;
use error::MapErrInto;
use error::RefErrorKind as REK;

pub struct RefLister {
    check_dead: bool,
    check_changed: bool,
    check_changed_content: bool,
    check_changed_permiss: bool,
}

impl RefLister {

    pub fn new() -> RefLister {
        RefLister::default()
    }

    pub fn check_dead(mut self, b: bool) -> RefLister {
        self.check_dead = b;
        self
    }

    pub fn check_changed(mut self, b: bool) -> RefLister {
        self.check_changed = b;
        self
    }

    pub fn check_changed_content(mut self, b: bool) -> RefLister {
        self.check_changed_content = b;
        self
    }

    pub fn check_changed_permiss(mut self, b: bool) -> RefLister {
        self.check_changed_permiss = b;
        self
    }

}

impl Default for RefLister {

    fn default() -> RefLister {
        RefLister {
            check_dead: false,
            check_changed: false,
            check_changed_content: false,
            check_changed_permiss: false,
        }
    }
}

impl Lister for RefLister {

    fn list<'b, I: Iterator<Item = FileLockEntry<'b>>>(&self, entries: I) -> Result<()> {

        debug!("Called list()");
        let (r, n) = entries.fold((Ok(()), 0), |(accu, i), entry| {
            debug!("fold({:?}, {:?})", accu, entry);
            let r = accu.and_then(|_| {
                    debug!("Listing Entry: {:?}", entry);
                    lister_fn(entry,
                              self.check_dead,
                              self.check_changed,
                              self.check_changed_content,
                              self.check_changed_permiss)
                        .and_then(|s| {
                            write!(stdout(), "{}\n", s)
                                .map_err(Box::new)
                                .map_err(|e| LEK::FormatError.into_error_with_cause(e))
                        })
                        .map_err_into(REK::RefToDisplayError)
                })
                .map(|_| ());
            (r, i + 1)
        });
        debug!("Iterated over {} entries", n);
        r.map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e)))
    }

}

fn lister_fn(fle: FileLockEntry,
             do_check_dead: bool,
             do_check_changed: bool,
             do_check_changed_content: bool,
             do_check_changed_permiss: bool) -> Result<String>
{
    Ref::from_filelockentry(fle)
        .map(|r| {
            let is_dead = if do_check_dead {
                if check_dead(&r) { "dead" } else { "alive" }
            } else {
                "not checked"
            };

            let is_changed = if do_check_changed {
                if check_changed(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            let is_changed_content = if do_check_changed_content {
                if check_changed_content(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            let is_changed_permiss = if do_check_changed_permiss {
                if check_changed_permiss(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            format!("{} | {} | {} | {} | {} | {}",
                    is_dead,
                    is_changed,
                    is_changed_content,
                    is_changed_permiss,
                    r.get_path_hash().unwrap_or_else(|| String::from("Cannot get hash")),
                    r.get_location())
        })
        .map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e)))
}

fn check_dead(r: &Ref) -> bool {
    match r.fs_link_exists() {
        Ok(b)  => b,
        Err(e) => {
            warn!("Could not check whether the ref {} exists on the FS:", r);
            trace_error(&e);

            // We continue here and tell the callee that this reference is dead, what is kind of
            // true actually, as we might not have access to it right now
            true
        },
    }
}

fn check_changed(r: &Ref) -> bool {
    check_changed_content(r) && check_changed_permiss(r)
}

fn check_changed_content(r: &Ref) -> bool {
    let eq = r.get_current_hash()
        .and_then(|hash| r.get_stored_hash().map(|stored| (hash, stored)))
        .map(|(hash, stored)| hash == stored);

    match eq {
        Ok(eq) => eq,
        Err(e) => {
            warn!("Could not check whether the ref {} changed on the FS:", r);
            trace_error(&e);

            // We continue here and tell the callee that this reference is unchanged
            false
        },
    }
}

fn check_changed_permiss(r: &Ref) -> bool {
    warn!("Permission changes tracking not supported yet.");
    false
}

<commit_msg>Remove unused variable<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::default::Default;
use std::io::stdout;
use std::io::Write;

use toml::Value;

use libimagentrylist::lister::Lister;
use libimagentrylist::result::Result;
use libimagerror::trace::trace_error;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagerror::into::IntoError;
use libimagentrylist::error::ListErrorKind as LEK;

use reference::Ref;
use error::MapErrInto;
use error::RefErrorKind as REK;

pub struct RefLister {
    check_dead: bool,
    check_changed: bool,
    check_changed_content: bool,
    check_changed_permiss: bool,
}

impl RefLister {

    pub fn new() -> RefLister {
        RefLister::default()
    }

    pub fn check_dead(mut self, b: bool) -> RefLister {
        self.check_dead = b;
        self
    }

    pub fn check_changed(mut self, b: bool) -> RefLister {
        self.check_changed = b;
        self
    }

    pub fn check_changed_content(mut self, b: bool) -> RefLister {
        self.check_changed_content = b;
        self
    }

    pub fn check_changed_permiss(mut self, b: bool) -> RefLister {
        self.check_changed_permiss = b;
        self
    }

}

impl Default for RefLister {

    fn default() -> RefLister {
        RefLister {
            check_dead: false,
            check_changed: false,
            check_changed_content: false,
            check_changed_permiss: false,
        }
    }
}

impl Lister for RefLister {

    fn list<'b, I: Iterator<Item = FileLockEntry<'b>>>(&self, entries: I) -> Result<()> {

        debug!("Called list()");
        let (r, n) = entries.fold((Ok(()), 0), |(accu, i), entry| {
            debug!("fold({:?}, {:?})", accu, entry);
            let r = accu.and_then(|_| {
                    debug!("Listing Entry: {:?}", entry);
                    lister_fn(entry,
                              self.check_dead,
                              self.check_changed,
                              self.check_changed_content,
                              self.check_changed_permiss)
                        .and_then(|s| {
                            write!(stdout(), "{}\n", s)
                                .map_err(Box::new)
                                .map_err(|e| LEK::FormatError.into_error_with_cause(e))
                        })
                        .map_err_into(REK::RefToDisplayError)
                })
                .map(|_| ());
            (r, i + 1)
        });
        debug!("Iterated over {} entries", n);
        r.map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e)))
    }

}

fn lister_fn(fle: FileLockEntry,
             do_check_dead: bool,
             do_check_changed: bool,
             do_check_changed_content: bool,
             do_check_changed_permiss: bool) -> Result<String>
{
    Ref::from_filelockentry(fle)
        .map(|r| {
            let is_dead = if do_check_dead {
                if check_dead(&r) { "dead" } else { "alive" }
            } else {
                "not checked"
            };

            let is_changed = if do_check_changed {
                if check_changed(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            let is_changed_content = if do_check_changed_content {
                if check_changed_content(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            let is_changed_permiss = if do_check_changed_permiss {
                if check_changed_permiss(&r) { "changed" } else { "unchanged" }
            } else {
                "not checked"
            };

            format!("{} | {} | {} | {} | {} | {}",
                    is_dead,
                    is_changed,
                    is_changed_content,
                    is_changed_permiss,
                    r.get_path_hash().unwrap_or_else(|| String::from("Cannot get hash")),
                    r.get_location())
        })
        .map_err(|e| LEK::FormatError.into_error_with_cause(Box::new(e)))
}

fn check_dead(r: &Ref) -> bool {
    match r.fs_link_exists() {
        Ok(b)  => b,
        Err(e) => {
            warn!("Could not check whether the ref {} exists on the FS:", r);
            trace_error(&e);

            // We continue here and tell the callee that this reference is dead, what is kind of
            // true actually, as we might not have access to it right now
            true
        },
    }
}

fn check_changed(r: &Ref) -> bool {
    check_changed_content(r) && check_changed_permiss(r)
}

fn check_changed_content(r: &Ref) -> bool {
    let eq = r.get_current_hash()
        .and_then(|hash| r.get_stored_hash().map(|stored| (hash, stored)))
        .map(|(hash, stored)| hash == stored);

    match eq {
        Ok(eq) => eq,
        Err(e) => {
            warn!("Could not check whether the ref {} changed on the FS:", r);
            trace_error(&e);

            // We continue here and tell the callee that this reference is unchanged
            false
        },
    }
}

fn check_changed_permiss(_: &Ref) -> bool {
    warn!("Permission changes tracking not supported yet.");
    false
}

<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::MapErrInto;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;

        use libimagstore::hook::position::HookPosition as HP;
        use libimagstore::hook::Hook;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");
        let colored      = !matches.is_present("no-color-output");

        Runtime::init_logger(is_debugging, is_verbose, colored);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let configpath = matches.value_of("config")
                                .map_or_else(|| rtp.clone(), PathBuf::from);

        let cfg = match Configuration::new(&configpath) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(mut cfg) => {
                if let Err(e) = cfg.override_config(get_override_specs(&matches)) {
                    error!("Could not apply config overrides");
                    trace_error(&e);

                    // TODO: continue question (interactive)
                }

                Some(cfg)
            }
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks : Vec<(Box<Hook>, &str, HP)> = vec![
                    (Box::new(DebugHook::new(HP::PreCreate))          , "debug", HP::PreCreate),
                    (Box::new(DebugHook::new(HP::PostCreate))         , "debug", HP::PostCreate),
                    (Box::new(DebugHook::new(HP::PreRetrieve))        , "debug", HP::PreRetrieve),
                    (Box::new(DebugHook::new(HP::PostRetrieve))       , "debug", HP::PostRetrieve),
                    (Box::new(DebugHook::new(HP::PreUpdate))          , "debug", HP::PreUpdate),
                    (Box::new(DebugHook::new(HP::PostUpdate))         , "debug", HP::PostUpdate),
                    (Box::new(DebugHook::new(HP::PreDelete))          , "debug", HP::PreDelete),
                    (Box::new(DebugHook::new(HP::PostDelete))         , "debug", HP::PostDelete),
                ];

                // If hook registration fails, trace the error and warn, but continue.
                for (hook, aspectname, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from(aspectname), hook) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err_into(RuntimeErrorKind::Instantiate)
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("no-color-output")
                .long("no-color")
                .help("Disable color output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("config-override")
                 .long("override-config")
                 .help("Override a configuration settings. Use 'key=value' pairs, where the key is a path in the TOML configuration. The value must be present in the configuration and be convertible to the type of the configuration setting. If the argument does not contain a '=', it gets ignored. Setting Arrays and Tables is not yet supported.")
                 .required(false)
                 .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("editor")
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool, colored: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Warn
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()).with_color(colored))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
    matches
        .values_of("config-override")
        .map(|values| {
             values
             .filter(|s| {
                 let b = s.contains("=");
                 if !b { warn!("override '{}' does not contain '=' - will be ignored!", s); }
                 b
             })
             .map(String::from)
             .collect()
        })
        .unwrap_or(vec![])
}

<commit_msg>Make arg names publicly available<commit_after>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use error::MapErrInto;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;

        use libimagstore::hook::position::HookPosition as HP;
        use libimagstore::hook::Hook;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");
        let colored      = !matches.is_present("no-color-output");

        Runtime::init_logger(is_debugging, is_verbose, colored);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let configpath = matches.value_of("config")
                                .map_or_else(|| rtp.clone(), PathBuf::from);

        let cfg = match Configuration::new(&configpath) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(mut cfg) => {
                if let Err(e) = cfg.override_config(get_override_specs(&matches)) {
                    error!("Could not apply config overrides");
                    trace_error(&e);

                    // TODO: continue question (interactive)
                }

                Some(cfg)
            }
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks : Vec<(Box<Hook>, &str, HP)> = vec![
                    (Box::new(DebugHook::new(HP::PreCreate))          , "debug", HP::PreCreate),
                    (Box::new(DebugHook::new(HP::PostCreate))         , "debug", HP::PostCreate),
                    (Box::new(DebugHook::new(HP::PreRetrieve))        , "debug", HP::PreRetrieve),
                    (Box::new(DebugHook::new(HP::PostRetrieve))       , "debug", HP::PostRetrieve),
                    (Box::new(DebugHook::new(HP::PreUpdate))          , "debug", HP::PreUpdate),
                    (Box::new(DebugHook::new(HP::PostUpdate))         , "debug", HP::PostUpdate),
                    (Box::new(DebugHook::new(HP::PreDelete))          , "debug", HP::PreDelete),
                    (Box::new(DebugHook::new(HP::PostDelete))         , "debug", HP::PostDelete),
                ];

                // If hook registration fails, trace the error and warn, but continue.
                for (hook, aspectname, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from(aspectname), hook) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err_into(RuntimeErrorKind::Instantiate)
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name(Runtime::arg_verbosity_name())
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_debugging_name())
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_no_color_output_name())
                .long("no-color")
                .help("Disable color output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name(Runtime::arg_config_name())
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_config_override_name())
                 .long("override-config")
                 .help("Override a configuration settings. Use 'key=value' pairs, where the key is a path in the TOML configuration. The value must be present in the configuration and be convertible to the type of the configuration setting. If the argument does not contain a '=', it gets ignored. Setting Arrays and Tables is not yet supported.")
                 .required(false)
                 .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_runtimepath_name())
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_storepath_name())
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name(Runtime::arg_editor_name())
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    pub fn arg_names() -> Vec<&'static str> {
        vec![
            Runtime::arg_verbosity_name(),
            Runtime::arg_debugging_name(),
            Runtime::arg_no_color_output_name(),
            Runtime::arg_config_name(),
            Runtime::arg_config_override_name(),
            Runtime::arg_runtimepath_name(),
            Runtime::arg_storepath_name(),
            Runtime::arg_editor_name(),
        ]
    }

    pub fn arg_verbosity_name() -> &'static str {
        "verbosity"
    }

    pub fn arg_debugging_name() -> &'static str {
        "debugging"
    }

    pub fn arg_no_color_output_name() -> &'static str {
        "no-color-output"
    }

    pub fn arg_config_name() -> &'static str {
        "config"
    }

    pub fn arg_config_override_name() -> &'static str {
        "config-override"
    }

    pub fn arg_runtimepath_name() -> &'static str {
        "runtimepath"
    }

    pub fn arg_storepath_name() -> &'static str {
        "storepath"
    }

    pub fn arg_editor_name() -> &'static str {
        "editor"
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool, colored: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Warn
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()).with_color(colored))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

fn get_override_specs(matches: &ArgMatches) -> Vec<String> {
    matches
        .values_of("config-override")
        .map(|values| {
             values
             .filter(|s| {
                 let b = s.contains("=");
                 if !b { warn!("override '{}' does not contain '=' - will be ignored!", s); }
                 b
             })
             .map(String::from)
             .collect()
        })
        .unwrap_or(vec![])
}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use ruru::AnyObject;
trait Wrap {
    fn wrap(self) -> AnyObject;
}

#[allow(unused_variables)]
pub mod storeid {
    use std::path::PathBuf;

    use ruru::{Class, Object, AnyObject, Boolean, RString, NilClass};

    use libimagstore::storeid::StoreId;

    wrappable_struct!(StoreId, StoreIdWrapper, STOREID_WRAPPER);
    class!(RStoreId);

    use store::Wrap;
    impl Wrap for StoreId {
        fn wrap(self) -> AnyObject {
            Class::from_existing("RStoreId").wrap_data(self, &*STOREID_WRAPPER)
        }
    }

    methods!(
        RStoreId,
        itself,

        fn r_storeid_new(base: RString, id: RString) -> AnyObject {
            let base = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(base) => base,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(Some(base), id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_new_baseless(id: RString) -> AnyObject {
            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(None, id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_without_base() -> RStoreId {
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().without_base();
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_with_base(base: RString) -> AnyObject {
            let base : PathBuf = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(pb) => pb,
                Err(e) => {
                    // TODO: Exception!
                    error!("Error: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().with_base(base);
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_into_pathbuf() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .clone()
                .into_pathbuf()
                // TODO: No unwraps
                .map(|pb| pb.to_str().map(String::from).unwrap())
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_exists() -> Boolean {
            Boolean::new(itself.get_data(&*STOREID_WRAPPER).exists())
        }

        fn r_storeid_to_str() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .to_str()
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_local() -> RString {
            let local = itself.get_data(&*STOREID_WRAPPER).local();
            let local = local.to_str().unwrap(); // TODO: No unwraps
            RString::new(local)
        }

    );

    pub fn setup() -> Class {
        let mut class = Class::new("RStoreId", None);
        class.define(|itself| {
            itself.def_self("new"          , r_storeid_new);
            itself.def_self("new_baseless" , r_storeid_new_baseless);

            itself.def("without_base"      , r_storeid_without_base);
            itself.def("with_base"         , r_storeid_with_base);
            itself.def("into_pathbuf"      , r_storeid_into_pathbuf);
            itself.def("exists"            , r_storeid_exists);
            itself.def("to_str"            , r_storeid_to_str);
            itself.def("local"             , r_storeid_local);
        });
        class
    }

}

#[allow(unused_variables)]
pub mod store {
    pub mod entry {
        use std::collections::BTreeMap;
        use std::error::Error;
        use std::ops::Deref;
        use std::ops::DerefMut;

        use ruru::{Class, Object, AnyObject, Boolean, RString, VM};

        use libimagstore::store::FileLockEntry as FLE;
        use libimagstore::store::EntryHeader;
        use libimagstore::store::EntryContent;
        use libimagstore::store::Entry;

        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use store::Wrap;

        pub struct FLECustomWrapper(Box<FLE<'static>>);

        impl Deref for FLECustomWrapper {
            type Target = Box<FLE<'static>>;

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

        impl DerefMut for FLECustomWrapper {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.0
            }
        }

        wrappable_struct!(FLECustomWrapper, FileLockEntryWrapper, FLE_WRAPPER);
        class!(RFileLockEntry);

        methods!(
            RFileLockEntry,
            itself,

            fn r_get_location() -> AnyObject {
                itself.get_data(&*FLE_WRAPPER).get_location().clone().wrap()
            }
        );

        wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
        class!(REntryHeader);

        impl Wrap for EntryHeader {
            fn wrap(self) -> AnyObject {
                Class::from_existing("REntryHeader").wrap_data(self, &*ENTRY_HEADER_WRAPPER)
            }
        }

        methods!(
            REntryHeader,
            itself,

            fn r_entry_header_new() -> AnyObject {
                EntryHeader::new().wrap()
            }

            fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false);
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
                    Ok(b) => Boolean::new(b),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false);
                    }
                }
            }

            fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

            fn r_entry_header_get(spec: RString) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

        );

        wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
        class!(REntryContent);

        wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
        class!(REntry);

        pub fn setup_filelockentry() -> Class {
            let mut class = Class::new("RFileLockEntry", None);
            class
        }

        pub fn setup_entryheader() -> Class {
            let mut class = Class::new("REntryHeader", None);
            class.define(|itself| {
                itself.def("insert", r_entry_header_insert);
                itself.def("set"   , r_entry_header_set);
                itself.def("read"  , r_entry_header_get);
            });
            class
        }

        pub fn setup_entrycontent() -> Class {
            let string = Class::from_existing("String");
            let mut class = Class::new("REntryContent", Some(&string));
            class
        }
    }

}

<commit_msg>Impl Wrap for EntryContent<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use ruru::AnyObject;
trait Wrap {
    fn wrap(self) -> AnyObject;
}

#[allow(unused_variables)]
pub mod storeid {
    use std::path::PathBuf;

    use ruru::{Class, Object, AnyObject, Boolean, RString, NilClass};

    use libimagstore::storeid::StoreId;

    wrappable_struct!(StoreId, StoreIdWrapper, STOREID_WRAPPER);
    class!(RStoreId);

    use store::Wrap;
    impl Wrap for StoreId {
        fn wrap(self) -> AnyObject {
            Class::from_existing("RStoreId").wrap_data(self, &*STOREID_WRAPPER)
        }
    }

    methods!(
        RStoreId,
        itself,

        fn r_storeid_new(base: RString, id: RString) -> AnyObject {
            let base = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(base) => base,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(Some(base), id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_new_baseless(id: RString) -> AnyObject {
            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(None, id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_without_base() -> RStoreId {
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().without_base();
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_with_base(base: RString) -> AnyObject {
            let base : PathBuf = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(pb) => pb,
                Err(e) => {
                    // TODO: Exception!
                    error!("Error: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().with_base(base);
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_into_pathbuf() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .clone()
                .into_pathbuf()
                // TODO: No unwraps
                .map(|pb| pb.to_str().map(String::from).unwrap())
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_exists() -> Boolean {
            Boolean::new(itself.get_data(&*STOREID_WRAPPER).exists())
        }

        fn r_storeid_to_str() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .to_str()
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_local() -> RString {
            let local = itself.get_data(&*STOREID_WRAPPER).local();
            let local = local.to_str().unwrap(); // TODO: No unwraps
            RString::new(local)
        }

    );

    pub fn setup() -> Class {
        let mut class = Class::new("RStoreId", None);
        class.define(|itself| {
            itself.def_self("new"          , r_storeid_new);
            itself.def_self("new_baseless" , r_storeid_new_baseless);

            itself.def("without_base"      , r_storeid_without_base);
            itself.def("with_base"         , r_storeid_with_base);
            itself.def("into_pathbuf"      , r_storeid_into_pathbuf);
            itself.def("exists"            , r_storeid_exists);
            itself.def("to_str"            , r_storeid_to_str);
            itself.def("local"             , r_storeid_local);
        });
        class
    }

}

#[allow(unused_variables)]
pub mod store {
    pub mod entry {
        use std::collections::BTreeMap;
        use std::error::Error;
        use std::ops::Deref;
        use std::ops::DerefMut;

        use ruru::{Class, Object, AnyObject, Boolean, RString, VM};

        use libimagstore::store::FileLockEntry as FLE;
        use libimagstore::store::EntryHeader;
        use libimagstore::store::EntryContent;
        use libimagstore::store::Entry;

        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use store::Wrap;

        pub struct FLECustomWrapper(Box<FLE<'static>>);

        impl Deref for FLECustomWrapper {
            type Target = Box<FLE<'static>>;

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

        impl DerefMut for FLECustomWrapper {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.0
            }
        }

        wrappable_struct!(FLECustomWrapper, FileLockEntryWrapper, FLE_WRAPPER);
        class!(RFileLockEntry);

        methods!(
            RFileLockEntry,
            itself,

            fn r_get_location() -> AnyObject {
                itself.get_data(&*FLE_WRAPPER).get_location().clone().wrap()
            }
        );

        wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
        class!(REntryHeader);

        impl Wrap for EntryHeader {
            fn wrap(self) -> AnyObject {
                Class::from_existing("REntryHeader").wrap_data(self, &*ENTRY_HEADER_WRAPPER)
            }
        }

        methods!(
            REntryHeader,
            itself,

            fn r_entry_header_new() -> AnyObject {
                EntryHeader::new().wrap()
            }

            fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false);
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
                    Ok(b) => Boolean::new(b),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false);
                    }
                }
            }

            fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

            fn r_entry_header_get(spec: RString) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

        );

        wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
        class!(REntryContent);

        impl Wrap for EntryContent {
            fn wrap(self) -> AnyObject {
                Class::from_existing("REntryContent").wrap_data(self, &*ENTRY_CONTENT_WRAPPER)
            }
        }

        wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
        class!(REntry);

        pub fn setup_filelockentry() -> Class {
            let mut class = Class::new("RFileLockEntry", None);
            class
        }

        pub fn setup_entryheader() -> Class {
            let mut class = Class::new("REntryHeader", None);
            class.define(|itself| {
                itself.def("insert", r_entry_header_insert);
                itself.def("set"   , r_entry_header_set);
                itself.def("read"  , r_entry_header_get);
            });
            class
        }

        pub fn setup_entrycontent() -> Class {
            let string = Class::from_existing("String");
            let mut class = Class::new("REntryContent", Some(&string));
            class
        }
    }

}

<|endoftext|>


<commit_before>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait Hook : Configureable + Debug {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook {
        fn post_read<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook {
        fn post_create<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook {
        fn post_retrieve<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook {
        fn post_update(&self, &FileLockEntry) -> HookResult<()>;
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug, PartialEq)]
    pub enum HookErrorKind {
        // ...
    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<commit_msg>Add HookErrorKind enums for Pre and Post errors<commit_after>use std::fmt::Debug;

use toml::Value;

use self::error::HookError;

pub type HookResult<T> = Result<T, HookError>;

pub trait Configureable {
    fn set_config(&mut self, cfg: Value);
}

pub trait Hook : Configureable + Debug {
}

pub mod read {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreReadHook : Hook {
        fn pre_read(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostReadHook : Hook {
        fn post_read<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod create {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreCreateHook : Hook {
        fn pre_create(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostCreateHook : Hook {
        fn post_create<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }

}

pub mod retrieve {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreRetrieveHook : Hook {
        fn pre_retrieve(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostRetrieveHook : Hook {
        fn post_retrieve<'a>(&self, FileLockEntry<'a>) -> HookResult<FileLockEntry<'a>>;
    }
}

pub mod update {
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreUpdateHook : Hook {
        fn pre_update(&self, &FileLockEntry) -> HookResult<()>;
    }

    pub trait PostUpdateHook : Hook {
        fn post_update(&self, &FileLockEntry) -> HookResult<()>;
    }
}

pub mod delete {
    use storeid::StoreId;
    use store::FileLockEntry;
    use super::HookResult;
    use super::Hook;

    pub trait PreDeleteHook : Hook {
        fn pre_delete(&self, &StoreId) -> HookResult<()>;
    }

    pub trait PostDeleteHook : Hook {
        fn post_delete(&self, &StoreId) -> HookResult<()>;
    }
}

pub mod error {
    use std::error::Error;
    use std::fmt::Error as FmtError;
    use std::clone::Clone;
    use std::fmt::{Display, Formatter};
    use std::convert::Into;

    /**
     * Kind of error
     */
    #[derive(Clone, Copy, Debug)]
    pub enum HookErrorKind {
        Pre(PreHookErrorKind),
        Post(PostHookErrorKind)
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PreHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PreHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Pre(self)
        }
    }

    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    pub enum PostHookErrorKind {
        // ...
    }

    impl Into<HookErrorKind> for PostHookErrorKind {
        fn into(self) -> HookErrorKind {
            HookErrorKind::Post(self)
        }
    }

    fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
        match e {
            _ => "",
        }
    }

    impl Display for HookErrorKind {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "{}", hook_error_type_as_str(self)));
            Ok(())
        }

    }

    /**
     * Error type
     */
    #[derive(Debug)]
    pub struct HookError {
        err_type: HookErrorKind,
        cause: Option<Box<Error>>,
    }

    impl HookError {

        /**
         * Build a new HookError from an HookErrorKind, optionally with cause
         */
        pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
            -> HookError
            {
                HookError {
                    err_type: errtype,
                    cause: cause,
                }
            }

        /**
         * Get the error type of this HookError
         */
        pub fn err_type(&self) -> HookErrorKind {
            self.err_type.clone()
        }

    }

    impl Display for HookError {

        fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
            try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
            Ok(())
        }

    }

    impl Error for HookError {

        fn description(&self) -> &str {
            hook_error_type_as_str(&self.err_type.clone())
        }

        fn cause(&self) -> Option<&Error> {
            self.cause.as_ref().map(|e| &**e)
        }

    }

}

<|endoftext|>