<commit_before>use architecture::*;
use goblin;
use loader::*;
use memory::backing::Memory;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The address where the first library will be loaded
const DEFAULT_LIB_BASE: u64 = 0x4000_0000;
/// The step in address between where we will load libraries.
const LIB_BASE_STEP: u64    = 0x0200_0000;

/// Loader which links together multiple Elf files. Currently only X86 supported.
#[derive(Debug)]
pub struct ElfLinker {
    /// The filename (path included) of the file we're loading.
    filename: PathBuf,
    /// A mapping from lib name (for example `libc.so.6`) to Elf.
    loaded: BTreeMap<String, Elf>,
    /// The current memory mapping.
    memory: Memory,
    /// A mapping of function symbol names to addresses
    symbols: BTreeMap<String, u64>,
    /// The address we will place the next library at.
    next_lib_address: u64,
    /// Functions as specified by the user
    user_functions: Vec<u64>
}

impl ElfLinker {
    /// Takes a path to an Elf and loads the Elf, its dependencies, and links
    /// them together.
    pub fn new(filename: &Path) -> Result<ElfLinker> {
        let mut file = File::open(filename)?;
        let mut buf = Vec::new();
        file.read_to_end(&mut buf)?;

        // get the endianness of this elf for the memory model
        let mut endian = Endian::Big;
        if let goblin::Object::Elf(elf_peek) = goblin::Object::parse(&buf)? {
            if elf_peek.header.endianness()?.is_little() {
                endian = Endian::Little;
            }
        }
        else {
            bail!(format!("{} was not an Elf", filename.to_str().unwrap()));
        }

        let mut elf_linker = ElfLinker {
            filename: filename.to_owned(),
            loaded: BTreeMap::new(),
            memory: Memory::new(endian),
            symbols: BTreeMap::new(),
            next_lib_address: DEFAULT_LIB_BASE,
            user_functions: Vec::new(),
        };

        elf_linker.load_elf(filename, 0)?;

        Ok(elf_linker)
    }

    /// Get the ELFs loaded and linked in this loader
    pub fn loaded(&self) -> &BTreeMap<String, Elf> {
        &self.loaded
    }

    /// Get the filename of the ELF we're loading
    pub fn filename(&self) -> &Path {
        &self.filename
    }

    /// Takes the path to an Elf, and a base address the Elf should be loaded
    /// at. Loads the Elf, all it's dependencies (DT_NEEDED), and then handles
    /// the supported relocations.
    pub fn load_elf(&mut self, filename: &Path, base_address: u64)
        -> Result<()> {

        // Does this file exist in the same directory as the original file?
        let mut base_path = match self.filename.as_path().parent() {
            Some(base_path) => base_path.to_path_buf(),
            None => PathBuf::new()
        };
        base_path.push(filename);

        let filename = if base_path.exists() {
            &base_path
        }
        else {
            filename
        };

        info!("Loading {} with base_address 0x{:x}",
            filename.to_str().unwrap(),
            base_address);
        let elf = Elf::from_file_with_base_address(filename, base_address)?;

        // Update our memory map based on what's in the Elf
        for (address, section) in elf.memory()?.sections() {
            self.memory.set_memory(*address,
                                   section.data().to_owned(),
                                   section.permissions());
        }

        // Add this Elf to the loaded Elfs
        let filename = filename.file_name()
                               .unwrap()
                               .to_str()
                               .unwrap()
                               .to_string();
        self.loaded.insert(filename.clone(), elf);

        {
            let ref elf = self.loaded[&filename];

            // Add its exported symbols to our symbols
            for symbol in elf.exported_symbols() {
                if self.symbols.get(symbol.name()).is_some() {
                    continue;
                }
                self.symbols.insert(
                    symbol.name().to_string(),
                    elf.base_address() + symbol.address()
                );
            }
        }

        // Ensure all shared objects we rely on are loaded
        for so_name in self.loaded[&filename].dt_needed()?.clone() {
            if self.loaded.get(&so_name).is_none() {
                self.next_lib_address += LIB_BASE_STEP;
                let next_lib_address = self.next_lib_address;
                self.load_elf(Path::new(&so_name), next_lib_address)?;
            }
        }

        match self.loaded[&filename].elf().header.e_machine {
            goblin::elf::header::EM_386 => self.relocations_x86(&filename)?,
            goblin::elf::header::EM_MIPS => self.relocations_mips(&filename)?,
            _ => bail!("relocations unsupported for target architecture")
        }

        Ok(())
    }

    fn relocations_x86(&mut self, filename: &str) -> Result<()> {
        // Process relocations
        let ref elf = self.loaded[filename];
        let dynsyms = elf.elf().dynsyms;
        let dynstrtab = elf.elf().dynstrtab;
        for reloc in elf.elf().dynrelas.iter().chain(
            elf.elf().dynrels.iter().chain(
                elf.elf().pltrelocs.iter())) {
            match reloc.r_type {
                goblin::elf::reloc::R_386_32 => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => bail!("Could not resolve symbol {}", sym_name)
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                }
                goblin::elf::reloc::R_386_GOT32 => {
                    bail!("R_386_GOT32");
                },
                goblin::elf::reloc::R_386_PLT32 => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    bail!("R_386_PLT32 {:?}:0x{:x}:{}",
                          self.filename,
                          reloc.r_offset,
                          sym_name);
                },
                goblin::elf::reloc::R_386_COPY => {
                    bail!("R_386_COPY");
                },
                goblin::elf::reloc::R_386_GLOB_DAT => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => {
                            warn!("Could not resolve symbol {}", sym_name);
                            continue
                        }
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                },
                goblin::elf::reloc::R_386_JMP_SLOT => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => bail!("Could not resolve symbol {}", sym_name)
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                },
                goblin::elf::reloc::R_386_RELATIVE => {
                    let value = self.memory.get32(reloc.r_offset as u64 + elf.base_address());
                    let value = match value {
                        Some(value) => elf.base_address() as u32 + value,
                        None => bail!("Invalid address for R_386_RELATIVE {:?}:{:x}",
                                      self.filename,
                                      reloc.r_offset)
                    };
                    self.memory.set32(reloc.r_offset as u64 + elf.base_address(), value)?;
                },
                goblin::elf::reloc::R_386_GOTPC => {
                    bail!("R_386_GOT_PC");
                },
                goblin::elf::reloc::R_386_TLS_TPOFF => {
                    warn!("Ignoring R_386_TLS_TPOFF Relocation");
                },
                goblin::elf::reloc::R_386_IRELATIVE => {
                    warn!("R_386_IRELATIVE {:?}:0x{:x} going unprocessed",
                          self.filename,
                          reloc.r_offset);
                }
                _ => bail!("unhandled relocation type {}", reloc.r_type)
            }
        }
        Ok(())
    }

    fn relocations_mips(&mut self, filename: &str) -> Result<()> {
        let elf = &self.loaded[filename];

        fn get_dynamic(elf: &Elf, tag: u64) -> Option<u64> {
            elf.elf().dynamic.and_then(|dynamic|
                dynamic.dyns
                    .iter()
                    .find(|dyn| dyn.d_tag == tag)
                    .map(|dyn| dyn.d_val))
        }

        // The number of local GOT entries. Also an index into the GOT
        // for the first external GOT entry.
        let local_gotno =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_LOCAL_GOTNO)
            .ok_or("Could not get DT_MIPS_LOCAL_GOTNO")?;

        // Index of the first dynamic symbol table entry that corresponds
        // to an entry in the GOT.
        let gotsym =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_GOTSYM)
            .ok_or("Could not get DT_MIPS_GOTSYM")?;

        // The number of entries in the dynamic symbol table
        let symtabno =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_SYMTABNO)
            .ok_or("Could not get DT_MIPS_SYMTABNO")?;

        // The address of the GOT section
        let pltgot =
            get_dynamic(elf, goblin::elf::dyn::DT_PLTGOT)
            .ok_or("Could not get DT_PLTGOT")?;

        // Start by adding the base address to all entries in the GOT
        for i in 0..(local_gotno + (symtabno - gotsym)) {
            let address = elf.base_address() + (i * 4) + pltgot;
            let value = self.memory.get32(address)
                .ok_or(format!("Could not get memory at address 0x{:x} for adding base address",
                    address))?;
            self.memory.set32(address, value.wrapping_add(elf.base_address() as u32))?;
        }

        let dynstrtab = elf.elf().dynstrtab;
        let dynsyms = elf.elf().dynsyms;
        let mut address = pltgot + elf.base_address() + (local_gotno * 4);
        for i in gotsym..(symtabno) {
            let sym = dynsyms.get(i as usize)
                .ok_or(format!("Could not get symbol {}", i))?;
            let symbol_name = dynstrtab.get(sym.st_name)
                .ok_or(format!("Could not get symbol name for {}", i))??;
            // Internal entries have already been relocated, so we only need to
            // relocate external entries
            if sym.st_shndx == 0 {
                if let Some(value) = self.symbols.get(symbol_name) {
                    self.memory.set32(address, *value as u32)?;

                    if symbol_name == "_rtld_global" {
                        println!("0x{:08x} symbol {} 0x{:08x} {:02} {} 0x{:x}",
                            address, i, sym.st_value, sym.st_shndx, symbol_name,
                            value);
                    }
                }
                else {
                    format!("Could not get symbol with name: \"{}\"",
                        symbol_name);
                }
            }
            address += 4;
        }

        // handle all relocation entries
        for dynrel in elf.elf().dynrels {
            if dynrel.r_type == goblin::elf::reloc::R_MIPS_REL32 {
                let value =
                    self.memory
                        .get32(dynrel.r_offset + elf.base_address())
                        .ok_or(format!("Could not load R_MIPS_REL32 at 0x{:x}",
                            dynrel.r_offset + elf.base_address()))?;
                self.memory.set32(
                    dynrel.r_offset + elf.base_address(),
                    value + (elf.base_address() as u32)
                )?;
            }
        }

        Ok(())
    }

    /// Inform the linker of a function at the given address.
    ///
    /// This function will be added to calls to `function_entries` and will be automatically
    /// lifted when calling `to_program`.
    pub fn add_user_function(&mut self, address: u64) {
        self.user_functions.push(address);
    }
}

impl Loader for ElfLinker {
    fn memory(&self) -> Result<Memory> {
        Ok(self.memory.clone())
    }

    fn function_entries(&self) -> Result<Vec<FunctionEntry>> {
        let mut function_entries = Vec::new();
        for loaded in &self.loaded {
            // let fe = loaded.1.function_entries()?;
            // for e in &fe {
            //     println!("{} 0x{:x}", loaded.0, e.address());
            // }
            function_entries.append(&mut loaded.1.function_entries()?);
        }
        for address in &self.user_functions {
            function_entries.push(FunctionEntry::new(*address, None));
        }
        Ok(function_entries)
    }

    // TODO Just maybe a bit too much unwrapping here.
    fn program_entry(&self) -> u64 {
        let filename = self.filename
                           .as_path()
                           .file_name()
                           .unwrap()
                           .to_str()
                           .unwrap();
        self.loaded[filename].program_entry()
    }

    fn architecture(&self) -> &Architecture {
        let filename = self.filename
                           .as_path()
                           .file_name()
                           .unwrap()
                           .to_str()
                           .unwrap();
        self.loaded[filename].architecture()
    }
}<commit_msg>Allow user to optionally turn off relocations in ElfLinker<commit_after>use architecture::*;
use goblin;
use loader::*;
use memory::backing::Memory;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The address where the first library will be loaded
const DEFAULT_LIB_BASE: u64 = 0x4000_0000;
/// The step in address between where we will load libraries.
const LIB_BASE_STEP: u64    = 0x0200_0000;

/// Loader which links together multiple Elf files.
///
/// Can do some rudimentary linking of binaries.
#[derive(Debug)]
pub struct ElfLinker {
    /// The filename (path included) of the file we're loading.
    filename: PathBuf,
    /// A mapping from lib name (for example `libc.so.6`) to Elf.
    loaded: BTreeMap<String, Elf>,
    /// The current memory mapping.
    memory: Memory,
    /// A mapping of function symbol names to addresses
    symbols: BTreeMap<String, u64>,
    /// The address we will place the next library at.
    next_lib_address: u64,
    /// Functions as specified by the user
    user_functions: Vec<u64>,
    /// If set, we will do relocations as we link
    do_relocations: bool
}

impl ElfLinker {
    /// Takes a path to an Elf and loads the Elf, its dependencies, and links
    /// them together.
    pub fn new(filename: &Path, do_relocations: bool) -> Result<ElfLinker> {
        let mut file = File::open(filename)?;
        let mut buf = Vec::new();
        file.read_to_end(&mut buf)?;

        // get the endianness of this elf for the memory model
        let mut endian = Endian::Big;
        if let goblin::Object::Elf(elf_peek) = goblin::Object::parse(&buf)? {
            if elf_peek.header.endianness()?.is_little() {
                endian = Endian::Little;
            }
        }
        else {
            bail!(format!("{} was not an Elf", filename.to_str().unwrap()));
        }

        let mut elf_linker = ElfLinker {
            filename: filename.to_owned(),
            loaded: BTreeMap::new(),
            memory: Memory::new(endian),
            symbols: BTreeMap::new(),
            next_lib_address: DEFAULT_LIB_BASE,
            user_functions: Vec::new(),
            do_relocations
        };

        elf_linker.load_elf(filename, 0)?;

        Ok(elf_linker)
    }

    /// Get the ELFs loaded and linked in this loader
    pub fn loaded(&self) -> &BTreeMap<String, Elf> {
        &self.loaded
    }

    /// Get the filename of the ELF we're loading
    pub fn filename(&self) -> &Path {
        &self.filename
    }

    /// Takes the path to an Elf, and a base address the Elf should be loaded
    /// at. Loads the Elf, all it's dependencies (DT_NEEDED), and then handles
    /// the supported relocations.
    pub fn load_elf(&mut self, filename: &Path, base_address: u64)
        -> Result<()> {

        // Does this file exist in the same directory as the original file?
        let mut base_path = match self.filename.as_path().parent() {
            Some(base_path) => base_path.to_path_buf(),
            None => PathBuf::new()
        };
        base_path.push(filename);

        let filename = if base_path.exists() {
            &base_path
        }
        else {
            filename
        };

        info!("Loading {} with base_address 0x{:x}",
            filename.to_str().unwrap(),
            base_address);
        let elf = Elf::from_file_with_base_address(filename, base_address)?;

        // Update our memory map based on what's in the Elf
        for (address, section) in elf.memory()?.sections() {
            self.memory.set_memory(*address,
                                   section.data().to_owned(),
                                   section.permissions());
        }

        // Add this Elf to the loaded Elfs
        let filename = filename.file_name()
                               .unwrap()
                               .to_str()
                               .unwrap()
                               .to_string();
        self.loaded.insert(filename.clone(), elf);

        {
            let ref elf = self.loaded[&filename];

            // Add its exported symbols to our symbols
            for symbol in elf.exported_symbols() {
                if self.symbols.get(symbol.name()).is_some() {
                    continue;
                }
                self.symbols.insert(
                    symbol.name().to_string(),
                    elf.base_address() + symbol.address()
                );
            }
        }

        // Ensure all shared objects we rely on are loaded
        for so_name in self.loaded[&filename].dt_needed()?.clone() {
            if self.loaded.get(&so_name).is_none() {
                self.next_lib_address += LIB_BASE_STEP;
                let next_lib_address = self.next_lib_address;
                self.load_elf(Path::new(&so_name), next_lib_address)?;
            }
        }

        if self.do_relocations {
            match self.loaded[&filename].elf().header.e_machine {
                goblin::elf::header::EM_386 =>
                    self.relocations_x86(&filename)?,
                goblin::elf::header::EM_MIPS =>
                    self.relocations_mips(&filename)?,
                _ => bail!("relocations unsupported for target architecture")
            }
        }

        Ok(())
    }

    fn relocations_x86(&mut self, filename: &str) -> Result<()> {
        // Process relocations
        let ref elf = self.loaded[filename];
        let dynsyms = elf.elf().dynsyms;
        let dynstrtab = elf.elf().dynstrtab;
        for reloc in elf.elf().dynrelas.iter().chain(
            elf.elf().dynrels.iter().chain(
                elf.elf().pltrelocs.iter())) {
            match reloc.r_type {
                goblin::elf::reloc::R_386_32 => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => bail!("Could not resolve symbol {}", sym_name)
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                }
                goblin::elf::reloc::R_386_GOT32 => {
                    bail!("R_386_GOT32");
                },
                goblin::elf::reloc::R_386_PLT32 => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    bail!("R_386_PLT32 {:?}:0x{:x}:{}",
                          self.filename,
                          reloc.r_offset,
                          sym_name);
                },
                goblin::elf::reloc::R_386_COPY => {
                    bail!("R_386_COPY");
                },
                goblin::elf::reloc::R_386_GLOB_DAT => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => {
                            warn!("Could not resolve symbol {}", sym_name);
                            continue
                        }
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                },
                goblin::elf::reloc::R_386_JMP_SLOT => {
                    let ref sym = dynsyms.get(reloc.r_sym)
                        .expect("Unable to resolve relocation symbol");
                    let sym_name = &dynstrtab[sym.st_name];
                    let value = match self.symbols.get(sym_name) {
                        Some(v) => v.to_owned() as u32,
                        None => bail!("Could not resolve symbol {}", sym_name)
                    };
                    self.memory.set32(
                        reloc.r_offset as u64 + elf.base_address(),
                        value
                    )?;
                },
                goblin::elf::reloc::R_386_RELATIVE => {
                    let value = self.memory.get32(reloc.r_offset as u64 + elf.base_address());
                    let value = match value {
                        Some(value) => elf.base_address() as u32 + value,
                        None => bail!("Invalid address for R_386_RELATIVE {:?}:{:x}",
                                      self.filename,
                                      reloc.r_offset)
                    };
                    self.memory.set32(reloc.r_offset as u64 + elf.base_address(), value)?;
                },
                goblin::elf::reloc::R_386_GOTPC => {
                    bail!("R_386_GOT_PC");
                },
                goblin::elf::reloc::R_386_TLS_TPOFF => {
                    warn!("Ignoring R_386_TLS_TPOFF Relocation");
                },
                goblin::elf::reloc::R_386_IRELATIVE => {
                    warn!("R_386_IRELATIVE {:?}:0x{:x} going unprocessed",
                          self.filename,
                          reloc.r_offset);
                }
                _ => bail!("unhandled relocation type {}", reloc.r_type)
            }
        }
        Ok(())
    }

    fn relocations_mips(&mut self, filename: &str) -> Result<()> {
        let elf = &self.loaded[filename];

        fn get_dynamic(elf: &Elf, tag: u64) -> Option<u64> {
            elf.elf().dynamic.and_then(|dynamic|
                dynamic.dyns
                    .iter()
                    .find(|dyn| dyn.d_tag == tag)
                    .map(|dyn| dyn.d_val))
        }

        // The number of local GOT entries. Also an index into the GOT
        // for the first external GOT entry.
        let local_gotno =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_LOCAL_GOTNO)
            .ok_or("Could not get DT_MIPS_LOCAL_GOTNO")?;

        // Index of the first dynamic symbol table entry that corresponds
        // to an entry in the GOT.
        let gotsym =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_GOTSYM)
            .ok_or("Could not get DT_MIPS_GOTSYM")?;

        // The number of entries in the dynamic symbol table
        let symtabno =
            get_dynamic(elf, goblin::elf::dyn::DT_MIPS_SYMTABNO)
            .ok_or("Could not get DT_MIPS_SYMTABNO")?;

        // The address of the GOT section
        let pltgot =
            get_dynamic(elf, goblin::elf::dyn::DT_PLTGOT)
            .ok_or("Could not get DT_PLTGOT")?;

        // Start by adding the base address to all entries in the GOT
        for i in 0..(local_gotno + (symtabno - gotsym)) {
            let address = elf.base_address() + (i * 4) + pltgot;
            let value = self.memory.get32(address)
                .ok_or(format!("Could not get memory at address 0x{:x} for adding base address",
                    address))?;
            self.memory.set32(address, value.wrapping_add(elf.base_address() as u32))?;
        }

        let dynstrtab = elf.elf().dynstrtab;
        let dynsyms = elf.elf().dynsyms;
        let mut address = pltgot + elf.base_address() + (local_gotno * 4);
        for i in gotsym..(symtabno) {
            let sym = dynsyms.get(i as usize)
                .ok_or(format!("Could not get symbol {}", i))?;
            let symbol_name = dynstrtab.get(sym.st_name)
                .ok_or(format!("Could not get symbol name for {}", i))??;
            // Internal entries have already been relocated, so we only need to
            // relocate external entries
            if sym.st_shndx == 0 {
                if let Some(value) = self.symbols.get(symbol_name) {
                    self.memory.set32(address, *value as u32)?;

                    if symbol_name == "_rtld_global" {
                        println!("0x{:08x} symbol {} 0x{:08x} {:02} {} 0x{:x}",
                            address, i, sym.st_value, sym.st_shndx, symbol_name,
                            value);
                    }
                }
                else {
                    format!("Could not get symbol with name: \"{}\"",
                        symbol_name);
                }
            }
            address += 4;
        }

        // handle all relocation entries
        for dynrel in elf.elf().dynrels {
            if dynrel.r_type == goblin::elf::reloc::R_MIPS_REL32 {
                let value =
                    self.memory
                        .get32(dynrel.r_offset + elf.base_address())
                        .ok_or(format!("Could not load R_MIPS_REL32 at 0x{:x}",
                            dynrel.r_offset + elf.base_address()))?;
                self.memory.set32(
                    dynrel.r_offset + elf.base_address(),
                    value + (elf.base_address() as u32)
                )?;
            }
        }

        Ok(())
    }

    /// Inform the linker of a function at the given address.
    ///
    /// This function will be added to calls to `function_entries` and will be automatically
    /// lifted when calling `to_program`.
    pub fn add_user_function(&mut self, address: u64) {
        self.user_functions.push(address);
    }
}

impl Loader for ElfLinker {
    fn memory(&self) -> Result<Memory> {
        Ok(self.memory.clone())
    }

    fn function_entries(&self) -> Result<Vec<FunctionEntry>> {
        let mut function_entries = Vec::new();
        for loaded in &self.loaded {
            // let fe = loaded.1.function_entries()?;
            // for e in &fe {
            //     println!("{} 0x{:x}", loaded.0, e.address());
            // }
            function_entries.append(&mut loaded.1.function_entries()?);
        }
        for address in &self.user_functions {
            function_entries.push(FunctionEntry::new(*address, None));
        }
        Ok(function_entries)
    }

    // TODO Just maybe a bit too much unwrapping here.
    fn program_entry(&self) -> u64 {
        let filename = self.filename
                           .as_path()
                           .file_name()
                           .unwrap()
                           .to_str()
                           .unwrap();
        self.loaded[filename].program_entry()
    }

    fn architecture(&self) -> &Architecture {
        let filename = self.filename
                           .as_path()
                           .file_name()
                           .unwrap()
                           .to_str()
                           .unwrap();
        self.loaded[filename].architecture()
    }
}<|endoftext|>


<commit_before>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    Docker,
};
use elasticsearch::{
    http::transport::{
        BuildError as TransportBuilderError, SingleNodeConnectionPool, TransportBuilder,
    },
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Elasticsearch, Error as ElasticsearchError,
};
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
use url::Url;

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
pub async fn initialize() -> Result<(), Error> {
    // let mtx = Arc::clone(&AVAILABLE);
    let mut docker = DockerWrapper::new();
    // let guard = AVAILABLE.lock().unwrap();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await
    } else {
        docker.cleanup().await
    }
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    Connection { source: BollardError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerError { source: BollardError },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image: String::from("docker.elastic.co/elasticsearch/elasticsearch:7.13.0"),
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        &docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(String::from(self.docker_image.clone())),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerError)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerError)?;

        sleep(Duration::from_secs(15)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let port = self.ports[0].0;
        // FIXME Hardcoded URL, need to extract it from self.
        let url = Url::parse(&format!("http://localhost:{}", port)).context(UrlParse)?;
        let conn_pool = SingleNodeConnectionPool::new(url);
        let transport = TransportBuilder::new(conn_pool)
            .disable_proxy()
            .build()
            .context(ElasticsearchTransport)?;
        let client = Elasticsearch::new(transport);

        let _ = client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(1)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<commit_msg>docker: Ensure the docker image is pulled before 'create_container'<commit_after>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::{
        BuildError as TransportBuilderError, SingleNodeConnectionPool, TransportBuilder,
    },
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Elasticsearch, Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
use url::Url;

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
pub async fn initialize() -> Result<(), Error> {
    // let mtx = Arc::clone(&AVAILABLE);
    let mut docker = DockerWrapper::new();
    // let guard = AVAILABLE.lock().unwrap();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await
    } else {
        docker.cleanup().await
    }
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    Connection { source: BollardError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerError { source: BollardError },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image: String::from("docker.elastic.co/elasticsearch/elasticsearch:7.13.0"),
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        &docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(String::from(self.docker_image.clone())),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerError)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerError)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerError)?;

        sleep(Duration::from_secs(15)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let port = self.ports[0].0;
        // FIXME Hardcoded URL, need to extract it from self.
        let url = Url::parse(&format!("http://localhost:{}", port)).context(UrlParse)?;
        let conn_pool = SingleNodeConnectionPool::new(url);
        let transport = TransportBuilder::new(conn_pool)
            .disable_proxy()
            .build()
            .context(ElasticsearchTransport)?;
        let client = Elasticsearch::new(transport);

        let _ = client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(1)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<|endoftext|>


<commit_before>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{
        Config as BollardConfig, CreateContainerOptions, ListContainersOptions,
        StartContainerOptions,
    },
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::BuildError as TransportBuilderError,
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::path::PathBuf;
use tokio::time::{sleep, Duration};

use crate::adapters::secondary::elasticsearch::remote;
use crate::adapters::secondary::elasticsearch::ElasticsearchStorageConfig;
use crate::domain::ports::secondary::remote::{Error as RemoteError, Remote};

pub async fn initialize() -> Result<(), Error> {
    initialize_with_param(true).await
}

/// Initializes a docker container for testing
/// It will see if a docker container is available with the default name
/// If there is no container, it will create one.
/// If there is already a container, and the parameter cleanup is true,
/// then all the indices found on that Elasticsearch are wiped out.
/// Once the container is available, a connection is attempted, to make
/// sure subsequent calls to that Elasticsearch will be successful.
pub async fn initialize_with_param(cleanup: bool) -> Result<(), Error> {
    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else if cleanup {
        docker.cleanup().await?;
    }
    let is_available = docker.is_container_available().await?;
    if !is_available {
        return Err(Error::Misc {
            msg: format!(
                "Cannot get docker {} available",
                docker.docker_config.container.name
            ),
        });
    }
    let config = ElasticsearchStorageConfig::default_testing();
    let _client = remote::connection_pool_url(&config.url)
        .conn(config)
        .await
        .context(ElasticsearchConnection)?;
    Ok(())
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    DockerConnection { source: BollardError },

    #[snafu(display("Connection to elasticsearch: {}", source))]
    ElasticsearchConnection { source: RemoteError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch client error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerEngine { source: BollardError },

    #[snafu(display("error: {}", msg))]
    Misc { msg: String },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerVersion {
    pub major: usize,
    pub minor: usize,
}

impl From<DockerVersion> for bollard::ClientVersion {
    fn from(version: DockerVersion) -> bollard::ClientVersion {
        bollard::ClientVersion {
            major_version: version.major,
            minor_version: version.minor,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContainerConfig {
    pub image: String,
    pub name: String,
    pub memory: i64,
    pub vars: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerConfig {
    pub container: ContainerConfig,
    pub timeout: u64,
    pub version: DockerVersion,
    pub container_wait: u64,
    pub elasticsearch_wait: u64,
    pub cleanup_wait: u64,
}

impl Default for DockerConfig {
    /// We retrieve the docker configuration from ./config/elasticsearch/default.
    fn default() -> Self {
        let config = common::config::config_from(
            &PathBuf::from("config"),
            &["elasticsearch"],
            None,
            None,
            None,
        );

        config
            .expect("cannot build the configuration for testing from config")
            .get("elasticsearch")
            .expect("expected elasticsearch section in configuration from config")
    }
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_config: DockerConfig,
}

impl DockerConfig {
    pub fn default_testing() -> Self {
        let config_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../config");

        let config = common::config::config_from(
            config_dir.as_path(),
            &["docker"],
            "testing",
            "MIMIR_TEST",
            None,
        );

        config
            .unwrap_or_else(|_| {
                panic!(
                    "cannot build the configuration for testing from {}",
                    config_dir.display(),
                )
            })
            .get("docker")
            .unwrap_or_else(|_| {
                panic!(
                    "expected docker section in configuration from {}",
                    config_dir.display(),
                )
            })
    }
}

// This is to create a new connection to the docker engine from a docker configuration.
impl TryFrom<DockerConfig> for Docker {
    type Error = Error;
    fn try_from(config: DockerConfig) -> Result<Self, Self::Error> {
        Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            config.timeout,
            &config.version.into(),
        )
        .context(DockerConnection)
    }
}

impl Default for DockerWrapper {
    fn default() -> Self {
        let elasticsearch_config = ElasticsearchStorageConfig::default_testing();
        let docker_config = DockerConfig::default_testing();

        let port = elasticsearch_config
            .url
            .port()
            .expect("expected port in elasticsearch url");

        let offset: u32 = (port - 9000).into();
        DockerWrapper {
            ports: vec![(9000 + offset, 9200), (9300 + offset, 9300)],
            docker_config,
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.docker_config.container.name is running
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::try_from(self.docker_config.clone())?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.docker_config.container.name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::try_from(self.docker_config.clone())?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.docker_config.container.name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.docker_config.container.name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                memory: Some(self.docker_config.container.memory * 1024 * 1024), // limit docker container to use 1GB of ram
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env = Some(self.docker_config.container.vars.clone()).and_then(|vars| {
                if vars.is_empty() {
                    None
                } else {
                    Some(vars)
                }
            });

            let config = BollardConfig {
                image: Some(self.docker_config.container.image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env,
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_config.container.image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerEngine)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerEngine)?;

            sleep(Duration::from_millis(self.docker_config.container_wait)).await;
        }
        let _ = docker
            .start_container(
                &self.docker_config.container.name,
                None::<StartContainerOptions<String>>,
            )
            .await
            .context(DockerEngine)?;

        sleep(Duration::from_millis(self.docker_config.elasticsearch_wait)).await;

        Ok(())
    }

    /// This function cleans up the Elasticsearch
    async fn cleanup(&mut self) -> Result<(), Error> {
        let pool = remote::connection_test_pool();

        let storage = pool
            .conn(ElasticsearchStorageConfig::default_testing())
            .await
            .context(ElasticsearchConnection)?;

        let _ = storage
            .client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        sleep(Duration::from_millis(self.docker_config.cleanup_wait)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::try_from(self.docker_config.clone()).expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.docker_config.container.name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.docker_config.container.name, options)
            .await
            .expect("remove container");
    }
}
<commit_msg>fix following review<commit_after>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{
        Config as BollardConfig, CreateContainerOptions, ListContainersOptions,
        StartContainerOptions,
    },
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::BuildError as TransportBuilderError,
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::path::PathBuf;
use tokio::time::{sleep, Duration};

use crate::adapters::secondary::elasticsearch::remote;
use crate::adapters::secondary::elasticsearch::ElasticsearchStorageConfig;
use crate::domain::ports::secondary::remote::{Error as RemoteError, Remote};

pub async fn initialize() -> Result<(), Error> {
    initialize_with_param(true).await
}

/// Initializes a docker container for testing
/// It will see if a docker container is available with the default name
/// If there is no container, it will create one.
/// If there is already a container, and the parameter cleanup is true,
/// then all the indices found on that Elasticsearch are wiped out.
/// Once the container is available, a connection is attempted, to make
/// sure subsequent calls to that Elasticsearch will be successful.
pub async fn initialize_with_param(cleanup: bool) -> Result<(), Error> {
    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else if cleanup {
        docker.cleanup().await?;
    }
    let is_available = docker.is_container_available().await?;
    if !is_available {
        return Err(Error::Misc {
            msg: format!(
                "Cannot get docker {} available",
                docker.docker_config.container.name
            ),
        });
    }
    let config = ElasticsearchStorageConfig::default_testing();
    let _client = remote::connection_pool_url(&config.url)
        .conn(config)
        .await
        .context(ElasticsearchConnection)?;
    Ok(())
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    DockerConnection { source: BollardError },

    #[snafu(display("Connection to elasticsearch: {}", source))]
    ElasticsearchConnection { source: RemoteError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch client error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerEngine { source: BollardError },

    #[snafu(display("error: {}", msg))]
    Misc { msg: String },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerVersion {
    pub major: usize,
    pub minor: usize,
}

impl From<DockerVersion> for bollard::ClientVersion {
    fn from(version: DockerVersion) -> bollard::ClientVersion {
        bollard::ClientVersion {
            major_version: version.major,
            minor_version: version.minor,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContainerConfig {
    pub image: String,
    pub name: String,
    pub memory: i64,
    pub vars: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerConfig {
    pub container: ContainerConfig,
    pub timeout: u64,
    pub version: DockerVersion,
    pub container_wait: u64,
    pub elasticsearch_wait: u64,
    pub cleanup_wait: u64,
}

impl Default for DockerConfig {
    /// We retrieve the docker configuration from ./config/elasticsearch/default.
    fn default() -> Self {
        let config = common::config::config_from(
            &PathBuf::from("config"),
            &["elasticsearch"],
            None,
            None,
            None,
        );

        config
            .expect("cannot build the configuration for testing from config")
            .get("elasticsearch")
            .expect("expected elasticsearch section in configuration from config")
    }
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_config: DockerConfig,
}

impl DockerConfig {
    pub fn default_testing() -> Self {
        let config_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../config");

        let config = common::config::config_from(
            config_dir.as_path(),
            &["docker"],
            "testing",
            "MIMIR_TEST",
            None,
        );

        config
            .unwrap_or_else(|_| {
                panic!(
                    "cannot build the configuration for testing from {}",
                    config_dir.display(),
                )
            })
            .get("docker")
            .unwrap_or_else(|_| {
                panic!(
                    "expected docker section in configuration from {}",
                    config_dir.display(),
                )
            })
    }
    pub fn connect(&self) -> Result<Docker, Error> {
        Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            self.timeout,
            &self.version.clone().into(),
        )
        .context(DockerConnection)
    }
}

impl Default for DockerWrapper {
    fn default() -> Self {
        let elasticsearch_config = ElasticsearchStorageConfig::default_testing();
        let docker_config = DockerConfig::default_testing();

        let port = elasticsearch_config
            .url
            .port()
            .expect("expected port in elasticsearch url");

        let offset: u32 = (port - 9000).into();
        DockerWrapper {
            ports: vec![(9000 + offset, 9200), (9300 + offset, 9300)],
            docker_config,
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.docker_config.container.name is running
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = self.docker_config.connect()?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.docker_config.container.name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = self.docker_config.connect()?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.docker_config.container.name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.docker_config.container.name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                memory: Some(self.docker_config.container.memory * 1024 * 1024),
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env = Some(self.docker_config.container.vars.clone()).and_then(|vars| {
                if vars.is_empty() {
                    None
                } else {
                    Some(vars)
                }
            });

            let config = BollardConfig {
                image: Some(self.docker_config.container.image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env,
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_config.container.image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerEngine)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerEngine)?;

            sleep(Duration::from_millis(self.docker_config.container_wait)).await;
        }
        let _ = docker
            .start_container(
                &self.docker_config.container.name,
                None::<StartContainerOptions<String>>,
            )
            .await
            .context(DockerEngine)?;

        sleep(Duration::from_millis(self.docker_config.elasticsearch_wait)).await;

        Ok(())
    }

    /// This function cleans up the Elasticsearch
    async fn cleanup(&mut self) -> Result<(), Error> {
        let pool = remote::connection_test_pool();

        let storage = pool
            .conn(ElasticsearchStorageConfig::default_testing())
            .await
            .context(ElasticsearchConnection)?;

        let _ = storage
            .client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = storage
            .client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .request_timeout(storage.config.timeout)
            .send()
            .await
            .context(ElasticsearchClient)?;

        sleep(Duration::from_millis(self.docker_config.cleanup_wait)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = self
            .docker_config
            .connect()
            .expect("docker engine connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.docker_config.container.name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.docker_config.container.name, options)
            .await
            .expect("remove container");
    }
}
<|endoftext|>


<commit_before>use super::guardian_lambda as gl;

extern crate rusoto_core;
extern crate rusoto_ssm;
extern crate rusoto_credential;

use rusoto_core::Region;
use rusoto_core::request::HttpClient;
use rusoto_core::DefaultCredentialsProvider;
use rusoto_ssm::SsmClient;
use rusoto_ssm::GetParameterRequest;
use crate::cleaner::rusoto_ssm::Ssm;
use rusoto_credential::ChainProvider;
use rusoto_credential::ProfileProvider;

pub fn lambda<A: gl::AbstractLambdaContext>(e: gl::LambdaInput, context: A) -> Result<gl::LambdaOutput, super::HandlerError> {
    let mut profileProvider: ProfileProvider = ProfileProvider::new().unwrap(); profileProvider.set_profile("mobile");
    let credentials = ChainProvider::with_profile_provider(profileProvider);
    let ssm_client = SsmClient::new_with(
        HttpClient::new().expect("failed to create request dispatcher"),
        credentials,
        Region::EuWest1
    );

    let req = GetParameterRequest {
        name: "/notifications/CODE/workers/cleaner.registration.db.url".to_string(),
        with_decryption: None,
    };
    match ssm_client.get_parameter(req).sync() {
        Ok(result) => {
            match result.parameter.and_then(|p| p.value) {
                Some(value) => info!("Value from SSM: {}", value),
                None => error!("No parameter found"),
            };
        }
        Err(error) => {
            error!("Error while fetching parameter: {}", error);
            return context.new_error("Empty first name");
        },
    }

    if 1 != 1 {
        error!("Empty first name in request {}", context.request_id());
        return context.new_error("Empty first name");
    }
    info!("Some message here");
    Ok(gl::LambdaOutput {})
}<commit_msg>Load Config from SSM<commit_after>use super::guardian_lambda as gl;

extern crate rusoto_core;
extern crate rusoto_ssm;
extern crate rusoto_credential;

use rusoto_core::Region;
use rusoto_core::request::HttpClient;
use rusoto_ssm::SsmClient;
use rusoto_ssm::GetParameterRequest;
use crate::cleaner::rusoto_ssm::Ssm;
use rusoto_credential::ChainProvider;
use rusoto_credential::ProfileProvider;
use std::collections::HashMap;
use std::env;
use self::rusoto_ssm::GetParametersByPathRequest;

fn env_var(key: String, default: String) -> String {
    env::var(key).unwrap_or(default)
}

fn fetch_config(credentials: ChainProvider) -> Result<HashMap<String, String>, String> {
    let mut config = HashMap::new();

    let app = env_var("App".to_string(), "db-cleaner".to_string());
    let stack = env_var("Stack".to_string(), "mobile".to_string());
    let stage = env_var("Stage".to_string(), "DEV".to_string());

    let ssm_client = SsmClient::new_with(
        HttpClient::new().expect("failed to create request dispatcher"),
        credentials,
        Region::EuWest1
    );

    let path: String = "/notifications/".to_owned() + &stage + "/workers";

    let req = GetParametersByPathRequest {
        max_results: None,
        next_token: None,
        parameter_filters: None,
        path: path.to_string(),
        recursive: Some(true),
        with_decryption: Some(true),

    };

    match ssm_client.get_parameters_by_path(req).sync() {
        Ok(result) => {
            match result.parameters {
                Some(parameters) => {
                    for parameter in parameters {
                        if parameter.name.is_some() && parameter.value.is_some() {
                            let prefixLength = path.len() + 1; // +1 for the extra slash
                            let name = parameter.name.unwrap()[prefixLength..].to_string();
                            config.insert(name, parameter.value.unwrap());
                        }
                    }
                },
                None => error!("No parameter found"),
            };
        }
        Err(error) => {
            error!("Error while fetching parameter: {}", error);
            return Err("Unable to fetch configuration".to_string())
        },
    }

    Ok(config)
}

pub fn lambda<A: gl::AbstractLambdaContext>(e: gl::LambdaInput, context: A) -> Result<gl::LambdaOutput, super::HandlerError> {
    let mut profileProvider: ProfileProvider = ProfileProvider::new().unwrap(); profileProvider.set_profile("mobile");
    let credentials = ChainProvider::with_profile_provider(profileProvider);
    let config = match fetch_config(credentials) {
        Ok(config) => {
            info!("Config loaded from SSM: {:?}", config);
            config
        },
        Err(e) => {
            error!("{}", e);
            return context.new_error(&e)
        }
    };

    info!("Some message here");
    Ok(gl::LambdaOutput {})
}<|endoftext|>


<commit_before>use macros::hashmap;
use std::collections::HashMap;

#[test]
fn test_empty() {
    let expected: HashMap<u32, u32> = HashMap::new();
    let computed: HashMap<u32, u32> = hashmap!();
    assert_eq!(computed, expected);
}

#[test]
#[ignore]
fn test_single() {
    let mut expected = HashMap::new();
    expected.insert(1, "one");
    assert_eq!(hashmap!(1 => "one"), expected);
}

#[test]
#[ignore]
fn test_no_trailing_comma() {
    let mut expected = HashMap::new();
    expected.insert(1, "one");
    expected.insert(2, "two");
    assert_eq!(hashmap!(1 => "one", 2 => "two"), expected);
}

#[test]
#[ignore]
fn test_trailing_comma() {
    let mut expected = HashMap::new();
    expected.insert('h', 89);
    expected.insert('a', 1);
    expected.insert('s', 19);
    expected.insert('h', 8);
    assert_eq!(
        hashmap!(
            'h' => 89,
            'a' => 1,
            's' => 19,
            'h' => 8,
        ),
        expected
    );
}

#[test]
#[ignore]
fn test_nested() {
    let mut expected = HashMap::new();
    expected.insert("non-empty", {
        let mut subhashmap = HashMap::new();
        subhashmap.insert(23, 623);
        subhashmap.insert(34, 21);
        subhashmap
    });
    expected.insert("empty", HashMap::new());
    assert_eq!(
        hashmap!(
            "non-empty" => hashmap!(
                23 => 623,
                34 => 21
            ),
            "empty" => hashmap!()
        ),
        expected
    );
}

#[test]
#[ignore]
fn test_compile_fails_comma_sep() {
    simple_trybuild::compile_fail("comma-sep.rs");
}

#[test]
#[ignore]
fn test_compile_fails_double_commas() {
    simple_trybuild::compile_fail("double-commas.rs");
}

#[test]
#[ignore]
fn test_compile_fails_only_comma() {
    simple_trybuild::compile_fail("only-comma.rs");
}

#[test]
#[ignore]
fn test_compile_fails_single_argument() {
    simple_trybuild::compile_fail("single-argument.rs");
}

#[test]
#[ignore]
fn test_compile_fails_triple_arguments() {
    simple_trybuild::compile_fail("triple-arguments.rs");
}

#[test]
#[ignore]
fn test_compile_fails_only_arrow() {
    simple_trybuild::compile_fail("only-arrow.rs");
}

#[test]
#[ignore]
fn test_compile_fails_two_arrows() {
    simple_trybuild::compile_fail("two-arrows.rs");
}

mod test {
    use macros::hashmap;
    #[test]
    #[ignore]
    fn type_not_in_scope() {
        let _expected: ::std::collections::HashMap<(), ()> = hashmap!();
    }
}

mod simple_trybuild {
    use std::path::PathBuf;
    use std::process::Command;

    pub fn compile_fail(file_name: &str) {
        let invalid_path: PathBuf = ["tests", "invalid"].iter().collect::<PathBuf>();

        let mut file_path = invalid_path.clone();
        file_path.push(file_name);
        assert!(
            file_path.exists(),
            "{:?} does not exist.",
            file_path.into_os_string()
        );

        let test_name = file_name.replace(".", "-");
        let macros_dir = ["..", "..", "target", "tests", "macros"]
            .iter()
            .collect::<PathBuf>();

        let result = Command::new("cargo")
            .current_dir(invalid_path)
            .arg("build")
            .arg("--offline")
            .arg("--target-dir")
            .arg(macros_dir)
            .arg("--bin")
            .arg(test_name)
            .output();

        if let Ok(result) = result {
            assert!(
                !result.status.success(),
                "Expected {:?} to fail to compile, but it succeeded.",
                file_path
            );
        } else {
            panic!("Running subprocess failed.");
        }
    }
}
<commit_msg>macros: Add tests to check for HashMap scoping (#938)<commit_after>use macros::hashmap;
use std::collections::HashMap;

#[test]
fn test_empty() {
    let expected: HashMap<u32, u32> = HashMap::new();
    let computed: HashMap<u32, u32> = hashmap!();
    assert_eq!(computed, expected);
}

#[test]
#[ignore]
fn test_single() {
    let mut expected = HashMap::new();
    expected.insert(1, "one");
    assert_eq!(hashmap!(1 => "one"), expected);
}

#[test]
#[ignore]
fn test_no_trailing_comma() {
    let mut expected = HashMap::new();
    expected.insert(1, "one");
    expected.insert(2, "two");
    assert_eq!(hashmap!(1 => "one", 2 => "two"), expected);
}

#[test]
#[ignore]
fn test_trailing_comma() {
    let mut expected = HashMap::new();
    expected.insert('h', 89);
    expected.insert('a', 1);
    expected.insert('s', 19);
    expected.insert('h', 8);
    assert_eq!(
        hashmap!(
            'h' => 89,
            'a' => 1,
            's' => 19,
            'h' => 8,
        ),
        expected
    );
}

#[test]
#[ignore]
fn test_nested() {
    let mut expected = HashMap::new();
    expected.insert("non-empty", {
        let mut subhashmap = HashMap::new();
        subhashmap.insert(23, 623);
        subhashmap.insert(34, 21);
        subhashmap
    });
    expected.insert("empty", HashMap::new());
    assert_eq!(
        hashmap!(
            "non-empty" => hashmap!(
                23 => 623,
                34 => 21
            ),
            "empty" => hashmap!()
        ),
        expected
    );
}

#[test]
#[ignore]
fn test_type_override() {
    // The macro should always use std::collections::HashMap and ignore crate::std::collections::HashMap
    mod std {
        pub mod collections {
            pub struct HashMap;

            impl HashMap {
                #[allow(dead_code)]
                pub fn new() -> Self {
                    panic!("Do not allow users to override which HashMap is used");
                }

                #[allow(dead_code)]
                pub fn insert<K, V>(&mut self, _key: K, _val: V) {
                    panic!("Do not allow users to override which HashMap is used");
                }
            }
        }
    }

    let _computed = hashmap!(1 => 2, 3 => 4);
}

#[test]
#[ignore]
fn test_compile_fails_comma_sep() {
    simple_trybuild::compile_fail("comma-sep.rs");
}

#[test]
#[ignore]
fn test_compile_fails_double_commas() {
    simple_trybuild::compile_fail("double-commas.rs");
}

#[test]
#[ignore]
fn test_compile_fails_only_comma() {
    simple_trybuild::compile_fail("only-comma.rs");
}

#[test]
#[ignore]
fn test_compile_fails_single_argument() {
    simple_trybuild::compile_fail("single-argument.rs");
}

#[test]
#[ignore]
fn test_compile_fails_triple_arguments() {
    simple_trybuild::compile_fail("triple-arguments.rs");
}

#[test]
#[ignore]
fn test_compile_fails_only_arrow() {
    simple_trybuild::compile_fail("only-arrow.rs");
}

#[test]
#[ignore]
fn test_compile_fails_two_arrows() {
    simple_trybuild::compile_fail("two-arrows.rs");
}

mod test {
    use macros::hashmap;
    #[test]
    #[ignore]
    fn type_not_in_scope() {
        let _expected: ::std::collections::HashMap<(), ()> = hashmap!();
    }
}

mod simple_trybuild {
    use std::path::PathBuf;
    use std::process::Command;

    pub fn compile_fail(file_name: &str) {
        let invalid_path: PathBuf = ["tests", "invalid"].iter().collect::<PathBuf>();

        let mut file_path = invalid_path.clone();
        file_path.push(file_name);
        assert!(
            file_path.exists(),
            "{:?} does not exist.",
            file_path.into_os_string()
        );

        let test_name = file_name.replace(".", "-");
        let macros_dir = ["..", "..", "target", "tests", "macros"]
            .iter()
            .collect::<PathBuf>();

        let result = Command::new("cargo")
            .current_dir(invalid_path)
            .arg("build")
            .arg("--offline")
            .arg("--target-dir")
            .arg(macros_dir)
            .arg("--bin")
            .arg(test_name)
            .output();

        if let Ok(result) = result {
            assert!(
                !result.status.success(),
                "Expected {:?} to fail to compile, but it succeeded.",
                file_path
            );
        } else {
            panic!("Running subprocess failed.");
        }
    }
}
<|endoftext|>


<commit_before>fn factorial(i : u64) -> u64 {
    match i {
      1 | 0 => 1,
      _ => i * factorial(i - 1),
    }
}

fn main(){
    let o: u64 = 5u64;
    let x = factorial(o);

    let y = format!("{:b}", x).len() as u64;
    println!("factorial({}) = {} ({:b} / {})", o, x, x, y);

    let q = 15u64;
    if y < q {
        println!("x < {} = {}", q, x);
    }

    let u = if x < 5u64 { 0i } else { 1i };
    println!("u = {}", u);

    let u1 = if x < 5u64 { 0i; } else { 1i; };
    println!("u1 = {}", u1);
}
<commit_msg>fix formatting (breaking change)<commit_after>fn factorial(i : u64) -> u64 {
    match i {
      1 | 0 => 1,
      _ => i * factorial(i - 1),
    }
}

fn main(){
    let o: u64 = 5u64;
    let x = factorial(o);

    let y = format!("{:b}", x).len() as u64;
    println!("factorial({}) = {} ({:b} / {})", o, x, x, y);

    let q = 15u64;
    if y < q {
        println!("x < {} = {}", q, x);
    }

    let u = if x < 5u64 { 0i32 } else { 1i32 };
    println!("u = {}", u);

    let u1 = if x < 5u64 { 0i32; } else { 1i32; };
    println!("u1 = {:?}", u1);
}
<|endoftext|>


<commit_before>use super::mode;
use super::util;
use std::collections::DList;
use std::collections::Deque;

#[deriving(Clone)]
#[deriving(Show)]
enum DOC {
    Nil,
    Append(Box<Doc>, Box<Doc>),
    Group(Box<Doc>),
    Nest(uint, Box<Doc>),
    Newline,
    Text(String),
}

pub type Doc = DOC;

fn fitting(mut cmds: DList<(uint,mode::Mode,Doc)>, mut rem:int) -> bool {
    let mut fits = true;

    loop {
        if rem < 0 {
            fits = false;
            break;
        }
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((i, mode, ref doc)) => match doc.clone() {
                Nil => {},
                Append(box x, box y) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    prefix.push((i, mode, y));
                    cmds.prepend(prefix);
                },
                Nest(j, box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i + j, mode, x));
                    cmds.prepend(prefix);
                },
                Text(str) => {
                    rem -= str.len() as int;
                },
                Newline => {
                    fits = true;
                },
                Group(box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    cmds.prepend(prefix);
                },
            }
        }
    }

    fits
}

fn best(width:uint, mut buf:DList<String>, x:Doc) -> DList<String> {
    let mut pos: uint = 0;
    let mut cmds: DList<(uint,mode::Mode,Doc)> = DList::new();

    cmds.push((0, mode::Break, x));

    loop {
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((i, mode, ref doc)) => match doc.clone() {
                Nil => {},
                Append(box x, box y) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    prefix.push((i, mode, y));
                    cmds.prepend(prefix);
                },
                Nest(j, box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i + j, mode, x));
                    cmds.prepend(prefix);
                },
                Text(str) => {
                    let mut prefix = DList::new();
                    prefix.push(str.clone());
                    buf.prepend(prefix);
                    pos += str.len();
                },
                Newline => {
                    let mut prefix = DList::new();
                    prefix.push(util::string::nl_spaces(i));
                    buf.prepend(prefix);
                    pos = i;
                },
                Group(ref x) => match mode {
                    mode::Flat => {
                        let mut prefix = DList::new();
                        prefix.push((i, mode::Flat, *x.clone()));
                        cmds.prepend(prefix);
                    },
                    mode::Break => {
                        let mut cmds_dup = cmds.clone();
                        let mut flat_prefix = DList::new();
                        flat_prefix.push((i, mode::Flat, *x.clone()));
                        cmds_dup.prepend(flat_prefix);
                        if fitting(cmds_dup, width as int - pos as int) {
                            let mut prefix = DList::new();
                            prefix.push((i, mode::Flat, *x.clone()));
                            cmds.prepend(prefix);
                        } else {
                            let mut prefix = DList::new();
                            prefix.push((i, mode::Break, *x.clone()));
                            cmds.prepend(prefix);
                        }
                    }
                }
            }
        }
    }

    buf
}

impl Doc {

    pub fn nil() -> Doc {
        Nil
    }

    pub fn append(self, e:Doc) -> Doc {
        match self {
            Nil => e,
            x => match e {
                Nil => x,
                y => Append(box x, box y)
            }
        }
    }

    pub fn nest(self, i:uint) -> Doc {
        Nest(i, box self)
    }

    pub fn text<S:Str>(s:S) -> Doc {
        Text(String::from_str(s.as_slice()))
    }

    pub fn newline() -> Doc {
        Newline
    }

    pub fn group(self) -> Doc {
        Group(box self)
    }

    pub fn concat(ds:&[Doc]) -> Doc {
        ds.iter().fold(Nil, |a, b| a.append(b.clone()))
    }

    pub fn as_string<T:ToString>(t:T) -> Doc {
        Doc::text(t.to_string())
    }

    pub fn render(self, w:uint) -> String {
        let strs = best(w, DList::new(), self);

        let mut result = String::new();
        for str in strs.iter().rev() {
            result.push_str(str.as_slice());
        }
        result
    }
}
<commit_msg>Remove ref; unbox<commit_after>use super::mode;
use super::util;
use std::collections::DList;
use std::collections::Deque;

#[deriving(Clone)]
#[deriving(Show)]
enum DOC {
    Nil,
    Append(Box<Doc>, Box<Doc>),
    Group(Box<Doc>),
    Nest(uint, Box<Doc>),
    Newline,
    Text(String),
}

pub type Doc = DOC;

fn fitting(mut cmds: DList<(uint,mode::Mode,Doc)>, mut rem:int) -> bool {
    let mut fits = true;

    loop {
        if rem < 0 {
            fits = false;
            break;
        }
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((i, mode, ref doc)) => match doc.clone() {
                Nil => {},
                Append(box x, box y) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    prefix.push((i, mode, y));
                    cmds.prepend(prefix);
                },
                Nest(j, box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i + j, mode, x));
                    cmds.prepend(prefix);
                },
                Text(str) => {
                    rem -= str.len() as int;
                },
                Newline => {
                    fits = true;
                },
                Group(box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    cmds.prepend(prefix);
                },
            }
        }
    }

    fits
}

fn best(width:uint, mut buf:DList<String>, x:Doc) -> DList<String> {
    let mut pos: uint = 0;
    let mut cmds: DList<(uint,mode::Mode,Doc)> = DList::new();

    cmds.push((0, mode::Break, x));

    loop {
        match cmds.pop_front() {
            None => {
                break;
            },
            Some((i, mode, ref doc)) => match doc.clone() {
                Nil => {},
                Append(box x, box y) => {
                    let mut prefix = DList::new();
                    prefix.push((i, mode, x));
                    prefix.push((i, mode, y));
                    cmds.prepend(prefix);
                },
                Nest(j, box x) => {
                    let mut prefix = DList::new();
                    prefix.push((i + j, mode, x));
                    cmds.prepend(prefix);
                },
                Text(str) => {
                    let mut prefix = DList::new();
                    prefix.push(str.clone());
                    buf.prepend(prefix);
                    pos += str.len();
                },
                Newline => {
                    let mut prefix = DList::new();
                    prefix.push(util::string::nl_spaces(i));
                    buf.prepend(prefix);
                    pos = i;
                },
                Group(box x) => match mode {
                    mode::Flat => {
                        let mut prefix = DList::new();
                        prefix.push((i, mode::Flat, x));
                        cmds.prepend(prefix);
                    },
                    mode::Break => {
                        let mut cmds_dup = cmds.clone();
                        let mut flat_prefix = DList::new();
                        flat_prefix.push((i, mode::Flat, x.clone()));
                        cmds_dup.prepend(flat_prefix);
                        if fitting(cmds_dup, width as int - pos as int) {
                            let mut prefix = DList::new();
                            prefix.push((i, mode::Flat, x));
                            cmds.prepend(prefix);
                        } else {
                            let mut prefix = DList::new();
                            prefix.push((i, mode::Break, x));
                            cmds.prepend(prefix);
                        }
                    }
                }
            }
        }
    }

    buf
}

impl Doc {

    pub fn nil() -> Doc {
        Nil
    }

    pub fn append(self, e:Doc) -> Doc {
        match self {
            Nil => e,
            x => match e {
                Nil => x,
                y => Append(box x, box y)
            }
        }
    }

    pub fn nest(self, i:uint) -> Doc {
        Nest(i, box self)
    }

    pub fn text<S:Str>(s:S) -> Doc {
        Text(String::from_str(s.as_slice()))
    }

    pub fn newline() -> Doc {
        Newline
    }

    pub fn group(self) -> Doc {
        Group(box self)
    }

    pub fn concat(ds:&[Doc]) -> Doc {
        ds.iter().fold(Nil, |a, b| a.append(b.clone()))
    }

    pub fn as_string<T:ToString>(t:T) -> Doc {
        Doc::text(t.to_string())
    }

    pub fn render(self, w:uint) -> String {
        let strs = best(w, DList::new(), self);

        let mut result = String::new();
        for str in strs.iter().rev() {
            result.push_str(str.as_slice());
        }
        result
    }
}
<|endoftext|>