<commit_before>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nes::cpu::CPU;
use std::io::Cursor;

// Memory partition sizes (physical).
// TODO: Calculate based on ranges below.
pub const RAM_SIZE                : usize = 0x800;
pub const PPU_CTRL_REGISTERS_SIZE : usize = 0x8;
pub const MISC_CTRL_REGISTERS_SIZE: usize = 0x20;
pub const EXPANSION_ROM_SIZE      : usize = 0x1FE0;
pub const SRAM_SIZE               : usize = 0x2000;
pub const PRG_ROM_SIZE            : usize = 0x4000;

// Partitioned virtual memory map bounds.
pub const RAM_START_ADDR                 : usize = 0x0;
pub const RAM_END_ADDR                   : usize = 0x7FF;
pub const RAM_MIRROR_START               : usize = 0x800;
pub const RAM_MIRROR_END                 : usize = 0x1FFF;
pub const PPU_CTRL_REGISTERS_START       : usize = 0x2000;
pub const PPU_CTRL_REGISTERS_END         : usize = 0x2007;
pub const PPU_CTRL_REGISTERS_MIRROR_START: usize = 0x2008;
pub const PPU_CTRL_REGISTERS_MIRROR_END  : usize = 0x3FFF;
pub const MISC_CTRL_REGISTERS_START      : usize = 0x4000;
pub const MISC_CTRL_REGISTERS_END        : usize = 0x401F;
pub const EXPANSION_ROM_START            : usize = 0x4020;
pub const EXPANSION_ROM_END              : usize = 0x5FFF;
pub const SRAM_START                     : usize = 0x6000;
pub const SRAM_END                       : usize = 0x7FFF;
pub const PRG_ROM_1_START                : usize = 0x8000;
pub const PRG_ROM_1_END                  : usize = 0xBFFF;
pub const PRG_ROM_2_START                : usize = 0xC000;
pub const PRG_ROM_2_END                  : usize = 0xFFFF;

// Constants for additional structures.
pub const TRAINER_START: usize = 0x7000;
pub const TRAINER_SIZE : usize = 512;

/// Partitioned physical memory layout for CPU memory. These fields are not
/// meant to be accessed directly by the CPU implementation and are instead
/// accessed through a read function that handles memory mapping.
///
/// NOTE: Currently all memory is allocated on the stack. This may not work well
/// for systems with a small stack and slices should be boxed up.
pub struct Memory {
    // 2kB of internal RAM for which it's use is entirely up to the programmer.
    ram: [u8; RAM_SIZE],

    // Contains PPU registers that allow the running application to communicate
    // with the PPU.
    ppu_ctrl_registers: [u8; PPU_CTRL_REGISTERS_SIZE],

    // Contains NES APU and I/O registers. Also allows use of APU and I/O
    // functionality that is normally disabled.
    misc_ctrl_registers: [u8; MISC_CTRL_REGISTERS_SIZE],

    expansion_rom: [u8; EXPANSION_ROM_SIZE],

    // 8kB of static RAM.
    sram: [u8; SRAM_SIZE],

    // PRG-ROM bank 1.
    prg_rom_1: [u8; PRG_ROM_SIZE],

    // PRG-ROM bank 2. Execution starts here.
    prg_rom_2: [u8; PRG_ROM_SIZE]
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            ram: [0; RAM_SIZE],
            ppu_ctrl_registers: [0; PPU_CTRL_REGISTERS_SIZE],
            misc_ctrl_registers: [0; MISC_CTRL_REGISTERS_SIZE],
            expansion_rom: [0; EXPANSION_ROM_SIZE],
            sram: [0; SRAM_SIZE],
            prg_rom_1: [0; PRG_ROM_SIZE],
            prg_rom_2: [0; PRG_ROM_SIZE]
        }
    }

    /// Reads an unsigned 8-bit byte value located at the given virtual address.
    #[inline(always)]
    pub fn read_u8(&mut self, addr: usize) -> u8 {
        let (bank, idx) = self.map(addr);
        bank[idx]
    }

    /// Writes an unsigned 8-bit byte value to the given virtual address.
    #[inline(always)]
    pub fn write_u8(&mut self, addr: usize, val: u8) {
        let (bank, idx) = self.map(addr);
        bank[idx] = val;
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian).
    #[inline(always)]
    pub fn read_u16(&mut self, addr: usize) -> u16 {
        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![
            self.read_u8(addr),
            self.read_u8(addr + 1)
        ]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian) where the MSB is read at page start if the LSB is at
    /// the end of a page. This exists to properly emulate a hardware bug in the
    /// 2A03 where indirect jumps cannot fetch addresses outside it's own page.
    #[inline(always)]
    pub fn read_u16_wrapped_msb(&mut self, addr: usize) -> u16 {
        let lsb = self.read_u8(addr);
        let msb = if addr & 0xFF == 0xFF {
            self.read_u8(addr - 0xFF)
        } else {
            self.read_u8(addr + 1)
        };

        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![lsb, msb]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Writes an unsigned 16-bit byte value to the given virtual address
    /// (little-endian)
    #[inline(always)]
    pub fn write_u16(&mut self, addr: usize, val: u16) {
        let mut writer = vec![];
        writer.write_u16::<LittleEndian>(val).unwrap();
        self.write_u8(addr, writer[0]);
        self.write_u8(addr + 1, writer[1]);
    }

    /// Dumps the contents of a slice starting at a given address.
    pub fn memdump(&mut self, addr: usize, buf: &[u8]) {
        for i in 0..buf.len() {
            self.write_u8(addr + i, buf[i]);
        }
    }

    // Utility functions for managing the stack.

    /// Pushes a 16-bit number (usually an address) onto the stack.
    pub fn stack_push_u16(&mut self, cpu: &mut CPU, value: u16) {
        self.write_u16(cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(2);
    }

    /// Pops a 16-bit number (usually an address) off the stack.
    pub fn stack_pop_u16(&mut self, cpu: &mut CPU) -> u16 {
        cpu.sp = cpu.sp.wrapping_add(2);
        self.read_u16(cpu.sp as usize)
    }

    // Memory mapping functions.

    /// Returns true when the provided address is in the provided range
    /// (inclusive).
    ///
    /// NOTE: Inclusive ranges are in unstable rust and this function can be
    /// replaced once it lands in stable (RFC 1192).
    fn addr_in_range(&self, addr: usize, lower: usize, upper: usize) -> bool {
        addr >= lower && addr <= upper
    }

    /// Maps a given virtual address to a physical address internal to the
    /// emulator. Returns a memory buffer and index for physical memory access.
    ///
    /// TODO: Add permissions flag for special addresses?
    fn map(&mut self, addr: usize) -> (&mut [u8], usize) {
        // Address translation for accessing system memory. No modifications
        // need to be done to the address as it's at the start of addressable
        // memory.
        if self.addr_in_range(addr, RAM_START_ADDR, RAM_END_ADDR) {
            return (&mut self.ram, addr)
        }

        // Address translation for mirroring of system memory. System memory at
        // $0000-$07FF is mirrored at $0800-$0FFF, $1000-$17FF, and $1800-$1FFF
        // - attempting to access memory at, for example, $0173 is the same as
        // accessing memory at $0973, $1173, or $1973.
        if self.addr_in_range(addr, RAM_MIRROR_START, RAM_MIRROR_END) {
            let new_addr = addr % RAM_SIZE;
            return (&mut self.ram, new_addr)
        }

        // Address translation for accessing the PPU control registers.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_START,
                              PPU_CTRL_REGISTERS_END) {
            let new_addr = addr - PPU_CTRL_REGISTERS_START;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for mirroring of the PPU control registers. PPU
        // control at $2000-$2007 is mirrored 1023 times at $2008-$3FFF.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_MIRROR_START,
                              PPU_CTRL_REGISTERS_MIRROR_END) {
            let new_addr = (addr - PPU_CTRL_REGISTERS_START) %
                PPU_CTRL_REGISTERS_SIZE;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for miscellaneous registers (such as APU etc).
        if self.addr_in_range(addr, MISC_CTRL_REGISTERS_START,
                              MISC_CTRL_REGISTERS_END) {
            let new_addr = addr - MISC_CTRL_REGISTERS_START;
            return (&mut self.misc_ctrl_registers, new_addr)
        }

        // Address translation for accessing cartridge expansion ROM.
        if self.addr_in_range(addr, EXPANSION_ROM_START, EXPANSION_ROM_END) {
            let new_addr = addr - EXPANSION_ROM_START;
            return (&mut self.expansion_rom, new_addr)
        }

        // Address translation for accessing SRAM.
        if self.addr_in_range(addr, SRAM_START, SRAM_END) {
            let new_addr = addr - SRAM_START;
            return (&mut self.sram, new_addr)
        }

        // Address translation for accessing the program rom (bank 1).
        if self.addr_in_range(addr, PRG_ROM_1_START, PRG_ROM_1_END) {
            let new_addr = addr - PRG_ROM_1_START;
            return (&mut self.prg_rom_1, new_addr)
        }

        // Address translation for accessing the program rom (bank 2).
        if self.addr_in_range(addr, PRG_ROM_2_START, PRG_ROM_2_END) {
            let new_addr = addr - PRG_ROM_2_START;
            return (&mut self.prg_rom_2, new_addr)
        }

        panic!("Unable to map virtual address {:#X} to any physical address",
               addr);
    }
}
<commit_msg>Added missing stack offsets<commit_after>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use nes::cpu::CPU;
use std::io::Cursor;

// Memory partition sizes (physical).
// TODO: Calculate based on ranges below.
pub const RAM_SIZE                : usize = 0x800;
pub const PPU_CTRL_REGISTERS_SIZE : usize = 0x8;
pub const MISC_CTRL_REGISTERS_SIZE: usize = 0x20;
pub const EXPANSION_ROM_SIZE      : usize = 0x1FE0;
pub const SRAM_SIZE               : usize = 0x2000;
pub const PRG_ROM_SIZE            : usize = 0x4000;

// Partitioned virtual memory map bounds.
pub const RAM_START_ADDR                 : usize = 0x0;
pub const RAM_END_ADDR                   : usize = 0x7FF;
pub const RAM_MIRROR_START               : usize = 0x800;
pub const RAM_MIRROR_END                 : usize = 0x1FFF;
pub const PPU_CTRL_REGISTERS_START       : usize = 0x2000;
pub const PPU_CTRL_REGISTERS_END         : usize = 0x2007;
pub const PPU_CTRL_REGISTERS_MIRROR_START: usize = 0x2008;
pub const PPU_CTRL_REGISTERS_MIRROR_END  : usize = 0x3FFF;
pub const MISC_CTRL_REGISTERS_START      : usize = 0x4000;
pub const MISC_CTRL_REGISTERS_END        : usize = 0x401F;
pub const EXPANSION_ROM_START            : usize = 0x4020;
pub const EXPANSION_ROM_END              : usize = 0x5FFF;
pub const SRAM_START                     : usize = 0x6000;
pub const SRAM_END                       : usize = 0x7FFF;
pub const PRG_ROM_1_START                : usize = 0x8000;
pub const PRG_ROM_1_END                  : usize = 0xBFFF;
pub const PRG_ROM_2_START                : usize = 0xC000;
pub const PRG_ROM_2_END                  : usize = 0xFFFF;

// Constants for additional structures.
pub const TRAINER_START: usize = 0x7000;
pub const TRAINER_SIZE : usize = 512;

/// Partitioned physical memory layout for CPU memory. These fields are not
/// meant to be accessed directly by the CPU implementation and are instead
/// accessed through a read function that handles memory mapping.
///
/// NOTE: Currently all memory is allocated on the stack. This may not work well
/// for systems with a small stack and slices should be boxed up.
pub struct Memory {
    // 2kB of internal RAM for which it's use is entirely up to the programmer.
    ram: [u8; RAM_SIZE],

    // Contains PPU registers that allow the running application to communicate
    // with the PPU.
    ppu_ctrl_registers: [u8; PPU_CTRL_REGISTERS_SIZE],

    // Contains NES APU and I/O registers. Also allows use of APU and I/O
    // functionality that is normally disabled.
    misc_ctrl_registers: [u8; MISC_CTRL_REGISTERS_SIZE],

    expansion_rom: [u8; EXPANSION_ROM_SIZE],

    // 8kB of static RAM.
    sram: [u8; SRAM_SIZE],

    // PRG-ROM bank 1.
    prg_rom_1: [u8; PRG_ROM_SIZE],

    // PRG-ROM bank 2. Execution starts here.
    prg_rom_2: [u8; PRG_ROM_SIZE]
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            ram: [0; RAM_SIZE],
            ppu_ctrl_registers: [0; PPU_CTRL_REGISTERS_SIZE],
            misc_ctrl_registers: [0; MISC_CTRL_REGISTERS_SIZE],
            expansion_rom: [0; EXPANSION_ROM_SIZE],
            sram: [0; SRAM_SIZE],
            prg_rom_1: [0; PRG_ROM_SIZE],
            prg_rom_2: [0; PRG_ROM_SIZE]
        }
    }

    /// Reads an unsigned 8-bit byte value located at the given virtual address.
    #[inline(always)]
    pub fn read_u8(&mut self, addr: usize) -> u8 {
        let (bank, idx) = self.map(addr);
        bank[idx]
    }

    /// Writes an unsigned 8-bit byte value to the given virtual address.
    #[inline(always)]
    pub fn write_u8(&mut self, addr: usize, val: u8) {
        let (bank, idx) = self.map(addr);
        bank[idx] = val;
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian).
    #[inline(always)]
    pub fn read_u16(&mut self, addr: usize) -> u16 {
        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![
            self.read_u8(addr),
            self.read_u8(addr + 1)
        ]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Reads an unsigned 16-bit byte value at the given virtual address
    /// (little-endian) where the MSB is read at page start if the LSB is at
    /// the end of a page. This exists to properly emulate a hardware bug in the
    /// 2A03 where indirect jumps cannot fetch addresses outside it's own page.
    #[inline(always)]
    pub fn read_u16_wrapped_msb(&mut self, addr: usize) -> u16 {
        let lsb = self.read_u8(addr);
        let msb = if addr & 0xFF == 0xFF {
            self.read_u8(addr - 0xFF)
        } else {
            self.read_u8(addr + 1)
        };

        // Reads two bytes starting at the given address and parses them.
        let mut reader = Cursor::new(vec![lsb, msb]);
        reader.read_u16::<LittleEndian>().unwrap()
    }

    /// Writes an unsigned 16-bit byte value to the given virtual address
    /// (little-endian)
    #[inline(always)]
    pub fn write_u16(&mut self, addr: usize, val: u16) {
        let mut writer = vec![];
        writer.write_u16::<LittleEndian>(val).unwrap();
        self.write_u8(addr, writer[0]);
        self.write_u8(addr + 1, writer[1]);
    }

    /// Dumps the contents of a slice starting at a given address.
    pub fn memdump(&mut self, addr: usize, buf: &[u8]) {
        for i in 0..buf.len() {
            self.write_u8(addr + i, buf[i]);
        }
    }

    // Utility functions for managing the stack.

    /// Pushes a 16-bit number (usually an address) onto the stack.
    pub fn stack_push_u16(&mut self, cpu: &mut CPU, value: u16) {
        self.write_u16(0x100 + cpu.sp as usize, value);
        cpu.sp = cpu.sp.wrapping_sub(2);
    }

    /// Pops a 16-bit number (usually an address) off the stack.
    pub fn stack_pop_u16(&mut self, cpu: &mut CPU) -> u16 {
        cpu.sp = cpu.sp.wrapping_add(2);
        self.read_u16(0x100 + cpu.sp as usize)
    }

    // Memory mapping functions.

    /// Returns true when the provided address is in the provided range
    /// (inclusive).
    ///
    /// NOTE: Inclusive ranges are in unstable rust and this function can be
    /// replaced once it lands in stable (RFC 1192).
    fn addr_in_range(&self, addr: usize, lower: usize, upper: usize) -> bool {
        addr >= lower && addr <= upper
    }

    /// Maps a given virtual address to a physical address internal to the
    /// emulator. Returns a memory buffer and index for physical memory access.
    ///
    /// TODO: Add permissions flag for special addresses?
    fn map(&mut self, addr: usize) -> (&mut [u8], usize) {
        // Address translation for accessing system memory. No modifications
        // need to be done to the address as it's at the start of addressable
        // memory.
        if self.addr_in_range(addr, RAM_START_ADDR, RAM_END_ADDR) {
            return (&mut self.ram, addr)
        }

        // Address translation for mirroring of system memory. System memory at
        // $0000-$07FF is mirrored at $0800-$0FFF, $1000-$17FF, and $1800-$1FFF
        // - attempting to access memory at, for example, $0173 is the same as
        // accessing memory at $0973, $1173, or $1973.
        if self.addr_in_range(addr, RAM_MIRROR_START, RAM_MIRROR_END) {
            let new_addr = addr % RAM_SIZE;
            return (&mut self.ram, new_addr)
        }

        // Address translation for accessing the PPU control registers.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_START,
                              PPU_CTRL_REGISTERS_END) {
            let new_addr = addr - PPU_CTRL_REGISTERS_START;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for mirroring of the PPU control registers. PPU
        // control at $2000-$2007 is mirrored 1023 times at $2008-$3FFF.
        if self.addr_in_range(addr, PPU_CTRL_REGISTERS_MIRROR_START,
                              PPU_CTRL_REGISTERS_MIRROR_END) {
            let new_addr = (addr - PPU_CTRL_REGISTERS_START) %
                PPU_CTRL_REGISTERS_SIZE;
            return (&mut self.ppu_ctrl_registers, new_addr)
        }

        // Address translation for miscellaneous registers (such as APU etc).
        if self.addr_in_range(addr, MISC_CTRL_REGISTERS_START,
                              MISC_CTRL_REGISTERS_END) {
            let new_addr = addr - MISC_CTRL_REGISTERS_START;
            return (&mut self.misc_ctrl_registers, new_addr)
        }

        // Address translation for accessing cartridge expansion ROM.
        if self.addr_in_range(addr, EXPANSION_ROM_START, EXPANSION_ROM_END) {
            let new_addr = addr - EXPANSION_ROM_START;
            return (&mut self.expansion_rom, new_addr)
        }

        // Address translation for accessing SRAM.
        if self.addr_in_range(addr, SRAM_START, SRAM_END) {
            let new_addr = addr - SRAM_START;
            return (&mut self.sram, new_addr)
        }

        // Address translation for accessing the program rom (bank 1).
        if self.addr_in_range(addr, PRG_ROM_1_START, PRG_ROM_1_END) {
            let new_addr = addr - PRG_ROM_1_START;
            return (&mut self.prg_rom_1, new_addr)
        }

        // Address translation for accessing the program rom (bank 2).
        if self.addr_in_range(addr, PRG_ROM_2_START, PRG_ROM_2_END) {
            let new_addr = addr - PRG_ROM_2_START;
            return (&mut self.prg_rom_2, new_addr)
        }

        panic!("Unable to map virtual address {:#X} to any physical address",
               addr);
    }
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;

const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);

declare_clippy_lint! {
    /// **What it does:**
    ///
    /// Suggests the use of `const` in functions and methods where possible.
    ///
    /// **Why is this bad?**
    ///
    /// Not having the function const prevents callers of the function from being const as well.
    ///
    /// **Known problems:**
    ///
    /// Const functions are currently still being worked on, with some features only being available
    /// on nightly. This lint does not consider all edge cases currently and the suggestions may be
    /// incorrect if you are using this lint on stable.
    ///
    /// Also, the lint only runs one pass over the code. Consider these two non-const functions:
    ///
    /// ```rust
    /// fn a() -> i32 {
    ///     0
    /// }
    /// fn b() -> i32 {
    ///     a()
    /// }
    /// ```
    ///
    /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
    /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
    /// will suggest to make `b` const, too.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// # struct Foo {
    /// #     random_number: usize,
    /// # }
    /// # impl Foo {
    /// fn new() -> Self {
    ///     Self { random_number: 42 }
    /// }
    /// # }
    /// ```
    ///
    /// Could be a const fn:
    ///
    /// ```rust
    /// # struct Foo {
    /// #     random_number: usize,
    /// # }
    /// # impl Foo {
    /// const fn new() -> Self {
    ///     Self { random_number: 42 }
    /// }
    /// # }
    /// ```
    pub MISSING_CONST_FOR_FN,
    nursery,
    "Lint functions definitions that could be made `const fn`"
}

impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);

pub struct MissingConstForFn {
    msrv: Option<RustcVersion>,
}

impl MissingConstForFn {
    #[must_use]
    pub fn new(msrv: Option<RustcVersion>) -> Self {
        Self { msrv }
    }
}

impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'_>,
        kind: FnKind<'_>,
        _: &FnDecl<'_>,
        _: &Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
            return;
        }

        let def_id = cx.tcx.hir().local_def_id(hir_id);

        if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
            return;
        }

        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
            return;
        }

        // Perform some preliminary checks that rule out constness on the Clippy side. This way we
        // can skip the actual const check and return early.
        match kind {
            FnKind::ItemFn(_, generics, header, ..) => {
                let has_const_generic_params = generics
                    .params
                    .iter()
                    .any(|param| matches!(param.kind, GenericParamKind::Const { .. }));

                if already_const(header) || has_const_generic_params {
                    return;
                }
            },
            FnKind::Method(_, sig, ..) => {
                if trait_ref_of_method(cx, hir_id).is_some()
                    || already_const(sig.header)
                    || method_accepts_dropable(cx, sig.decl.inputs)
                {
                    return;
                }
            },
            FnKind::Closure => return,
        }

        let mir = cx.tcx.optimized_mir(def_id);

        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
            if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
                cx.tcx.sess.span_err(span, &err);
            }
        } else {
            span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
        }
    }
    extract_msrv_attr!(LateContext);
}

/// Returns true if any of the method parameters is a type that implements `Drop`. The method
/// can't be made const then, because `drop` can't be const-evaluated.
fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
    // If any of the params are droppable, return true
    param_tys.iter().any(|hir_ty| {
        let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
        has_drop(cx, ty_ty)
    })
}

// We don't have to lint on something that's already `const`
#[must_use]
fn already_const(header: hir::FnHeader) -> bool {
    header.constness == Constness::Const
}
<commit_msg>fix clippy<commit_after>use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;

const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);

declare_clippy_lint! {
    /// **What it does:**
    ///
    /// Suggests the use of `const` in functions and methods where possible.
    ///
    /// **Why is this bad?**
    ///
    /// Not having the function const prevents callers of the function from being const as well.
    ///
    /// **Known problems:**
    ///
    /// Const functions are currently still being worked on, with some features only being available
    /// on nightly. This lint does not consider all edge cases currently and the suggestions may be
    /// incorrect if you are using this lint on stable.
    ///
    /// Also, the lint only runs one pass over the code. Consider these two non-const functions:
    ///
    /// ```rust
    /// fn a() -> i32 {
    ///     0
    /// }
    /// fn b() -> i32 {
    ///     a()
    /// }
    /// ```
    ///
    /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
    /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
    /// will suggest to make `b` const, too.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// # struct Foo {
    /// #     random_number: usize,
    /// # }
    /// # impl Foo {
    /// fn new() -> Self {
    ///     Self { random_number: 42 }
    /// }
    /// # }
    /// ```
    ///
    /// Could be a const fn:
    ///
    /// ```rust
    /// # struct Foo {
    /// #     random_number: usize,
    /// # }
    /// # impl Foo {
    /// const fn new() -> Self {
    ///     Self { random_number: 42 }
    /// }
    /// # }
    /// ```
    pub MISSING_CONST_FOR_FN,
    nursery,
    "Lint functions definitions that could be made `const fn`"
}

impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);

pub struct MissingConstForFn {
    msrv: Option<RustcVersion>,
}

impl MissingConstForFn {
    #[must_use]
    pub fn new(msrv: Option<RustcVersion>) -> Self {
        Self { msrv }
    }
}

impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
    fn check_fn(
        &mut self,
        cx: &LateContext<'_>,
        kind: FnKind<'_>,
        _: &FnDecl<'_>,
        _: &Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
            return;
        }

        let def_id = cx.tcx.hir().local_def_id(hir_id);

        if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
            return;
        }

        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
            return;
        }

        // Perform some preliminary checks that rule out constness on the Clippy side. This way we
        // can skip the actual const check and return early.
        match kind {
            FnKind::ItemFn(_, generics, header, ..) => {
                let has_const_generic_params = generics
                    .params
                    .iter()
                    .any(|param| matches!(param.kind, GenericParamKind::Const { .. }));

                if already_const(header) || has_const_generic_params {
                    return;
                }
            },
            FnKind::Method(_, sig, ..) => {
                if trait_ref_of_method(cx, hir_id).is_some()
                    || already_const(sig.header)
                    || method_accepts_dropable(cx, sig.decl.inputs)
                {
                    return;
                }
            },
            FnKind::Closure => return,
        }

        let mir = cx.tcx.optimized_mir(def_id);

        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
            if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
                cx.tcx.sess.span_err(span, &err);
            }
        } else {
            span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
        }
    }
    extract_msrv_attr!(LateContext);
}

/// Returns true if any of the method parameters is a type that implements `Drop`. The method
/// can't be made const then, because `drop` can't be const-evaluated.
fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
    // If any of the params are droppable, return true
    param_tys.iter().any(|hir_ty| {
        let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
        has_drop(cx, ty_ty)
    })
}

// We don't have to lint on something that's already `const`
#[must_use]
fn already_const(header: hir::FnHeader) -> bool {
    header.constness == Constness::Const
}
<|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::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(mut cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::io::stdout;

        use clap::Shell;

        use libimagstore::hook::position::HookPosition as HP;
        use libimagstore::hook::Hook;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagstorestdhook::vcs::git::delete::DeleteHook as GitDeleteHook;
        use libimagstorestdhook::vcs::git::update::UpdateHook as GitUpdateHook;
        use libimagstorestdhook::vcs::git::store_unload::StoreUnloadHook as GitStoreUnloadHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.clone().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);

        match matches.value_of(Runtime::arg_generate_compl()) {
            Some(shell) => {
                debug!("Generating shell completion script, writing to stdout");
                let shell = shell.parse::<Shell>().unwrap(); // clap has our back here.
                cli_spec.gen_completions_to("fakename", shell, &mut stdout());
            },
            _ => debug!("Not generating shell completion script"),
        }

        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.clone(), 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);
                        };
                    }
                }
            }

            let sp = storepath;

            let hooks : Vec<(Box<Hook>, &str, HP)> = vec![
                (Box::new(GitDeleteHook::new(sp.clone(), HP::PostDelete)), "vcs", HP::PostDelete),
                (Box::new(GitUpdateHook::new(sp.clone(), HP::PostUpdate)), "vcs", HP::PostUpdate),
                (Box::new(GitStoreUnloadHook::new(sp)),                    "vcs", HP::StoreUnload),
            ];

            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 git 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))

            .arg(Arg::with_name(Runtime::arg_generate_compl())
                .long("generate-commandline-completion")
                .help("Generate the commandline completion for bash or zsh or fish")
                .required(false)
                .takes_value(true)
                .value_name("SHELL")
                .possible_values(&["bash", "fish", "zsh"]))

    }

    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"
    }

    pub fn arg_generate_compl() -> &'static str {
        "generate-completion"
    }

    /**
     * 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>Pass real app name<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::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(mut cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::io::stdout;

        use clap::Shell;

        use libimagstore::hook::position::HookPosition as HP;
        use libimagstore::hook::Hook;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagstorestdhook::vcs::git::delete::DeleteHook as GitDeleteHook;
        use libimagstorestdhook::vcs::git::update::UpdateHook as GitUpdateHook;
        use libimagstorestdhook::vcs::git::store_unload::StoreUnloadHook as GitStoreUnloadHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.clone().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);

        match matches.value_of(Runtime::arg_generate_compl()) {
            Some(shell) => {
                debug!("Generating shell completion script, writing to stdout");
                let shell   = shell.parse::<Shell>().unwrap(); // clap has our back here.
                let appname = String::from(cli_spec.get_name());
                cli_spec.gen_completions_to(appname, shell, &mut stdout());
            },
            _ => debug!("Not generating shell completion script"),
        }

        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.clone(), 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);
                        };
                    }
                }
            }

            let sp = storepath;

            let hooks : Vec<(Box<Hook>, &str, HP)> = vec![
                (Box::new(GitDeleteHook::new(sp.clone(), HP::PostDelete)), "vcs", HP::PostDelete),
                (Box::new(GitUpdateHook::new(sp.clone(), HP::PostUpdate)), "vcs", HP::PostUpdate),
                (Box::new(GitStoreUnloadHook::new(sp)),                    "vcs", HP::StoreUnload),
            ];

            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 git 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))

            .arg(Arg::with_name(Runtime::arg_generate_compl())
                .long("generate-commandline-completion")
                .help("Generate the commandline completion for bash or zsh or fish")
                .required(false)
                .takes_value(true)
                .value_name("SHELL")
                .possible_values(&["bash", "fish", "zsh"]))

    }

    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"
    }

    pub fn arg_generate_compl() -> &'static str {
        "generate-completion"
    }

    /**
     * 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
//

#[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);

    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, Array, Hash, Fixnum, Float, Symbol, AnyObject, Boolean, RString, VM};

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

        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);

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

            fn r_entry_header_new() -> AnyObject {
                Class::from_existing("REntryHeader")
                    .wrap_data(EntryHeader::new(), &*ENTRY_HEADER_WRAPPER)
            }

            fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
                use toml::Value;
                use ruru::types::ValueType;
                use ruby_utils::IntoToml;

                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);
                    }
                }
            }

        );

        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
        }

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

}

<commit_msg>Implement r_entry_header_set()<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
//

#[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);

    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;

        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);

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

            fn r_entry_header_new() -> AnyObject {
                Class::from_existing("REntryHeader")
                    .wrap_data(EntryHeader::new(), &*ENTRY_HEADER_WRAPPER)
            }

            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();
                    }
                }
            }

        );

        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
        }

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

}

<|endoftext|>