<commit_before>use std::env;
use std::fs;
use std::path::Path;

const LINUX_CLANG_DIRS: &'static [&'static str] = &["/usr/lib", "/usr/lib/llvm", "/usr/lib64/llvm", "/usr/lib/x86_64-linux-gnu"];
const MAC_CLANG_DIR: &'static str = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib";
const WIN_CLANG_DIRS: &'static [&'static str] = &["C:\\Program Files\\LLVM\\bin", "C:\\Program Files\\LLVM\\lib"];

fn path_exists(path: &Path) -> bool {
    match fs::metadata(path) {
        Ok(_) => true,
        Err(_) => false
    }
}

fn main() {
    let use_static_lib = env::var_os("LIBCLANG_STATIC").is_some() || cfg!(feature = "static");

    let possible_clang_dirs = if let Ok(dir) = env::var("LIBCLANG_PATH") {
        vec![dir]
    } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
        LINUX_CLANG_DIRS.iter().map(ToString::to_string).collect()
    } else if cfg!(target_os = "macos") {
        vec![MAC_CLANG_DIR.to_string()]
    } else if cfg!(target_os = "windows") {
		WIN_CLANG_DIRS.iter().map(ToString::to_string).collect()
    } else {
        panic!("Platform not supported");
    };

    let clang_lib = if cfg!(target_os = "windows") {
			format!("libclang{}", env::consts::DLL_SUFFIX)
		} else {
			format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)
		};

    let maybe_clang_dir = possible_clang_dirs.iter().filter_map(|candidate_dir| {
        let clang_dir = Path::new(candidate_dir);
        let clang_path = clang_dir.join(clang_lib.clone());
        if path_exists(&*clang_path) {
            Some(clang_dir)
        } else {
            None
        }
    }).next();

    macro_rules! qw {
        ($($i:ident)*) => (vec!($(stringify!($i)),*));
    }

    if let Some(clang_dir) = maybe_clang_dir {
        if use_static_lib {
            let libs = qw![
                LLVMAnalysis
                LLVMBitReader
                LLVMCore
                LLVMLTO
                LLVMLinker
                LLVMMC
                LLVMMCParser
                LLVMObjCARCOpts
                LLVMObject
                LLVMOption
                LLVMScalarOpts
                LLVMSupport
                LLVMTarget
                LLVMTransformUtils
                LLVMVectorize
                LLVMipa
                LLVMipo
                clang
                clangARCMigrate
                clangAST
                clangASTMatchers
                clangAnalysis
                clangBasic
                clangDriver
                clangEdit
                clangFormat
                clangFrontend
                clangIndex
                clangLex
                clangParse
                clangRewrite
                clangRewriteFrontend
                clangSema
                clangSerialization
                clangStaticAnalyzerCheckers
                clangStaticAnalyzerCore
                clangStaticAnalyzerFrontend
                clangTooling
            ];

            print!("cargo:rustc-flags=");
            for lib in libs {
                print!("-l static={} ", lib);
            }
            println!("-L {} -l ncursesw -l z -l stdc++", clang_dir.to_str().unwrap());
        } else{
            println!("cargo:rustc-link-search=native={}", clang_dir.to_str().unwrap());
            println!("cargo:rustc-link-lib=dylib=clang");
        }
    } else {
        panic!("Unable to find {}", clang_lib);
    }
}
<commit_msg>Add Debian/Ubuntu locations to clang search paths<commit_after>use std::env;
use std::fs;
use std::path::Path;

const LINUX_CLANG_DIRS: &'static [&'static str] = &[
    "/usr/lib",
    "/usr/lib/llvm",
    "/usr/lib/llvm-3.4/lib",
    "/usr/lib/llvm-3.5/lib",
    "/usr/lib/llvm-3.6/lib",
    "/usr/lib64/llvm",
    "/usr/lib/x86_64-linux-gnu",
];
const MAC_CLANG_DIR: &'static str = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib";
const WIN_CLANG_DIRS: &'static [&'static str] = &["C:\\Program Files\\LLVM\\bin", "C:\\Program Files\\LLVM\\lib"];

fn path_exists(path: &Path) -> bool {
    match fs::metadata(path) {
        Ok(_) => true,
        Err(_) => false
    }
}

fn main() {
    let use_static_lib = env::var_os("LIBCLANG_STATIC").is_some() || cfg!(feature = "static");

    let possible_clang_dirs = if let Ok(dir) = env::var("LIBCLANG_PATH") {
        vec![dir]
    } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
        LINUX_CLANG_DIRS.iter().map(ToString::to_string).collect()
    } else if cfg!(target_os = "macos") {
        vec![MAC_CLANG_DIR.to_string()]
    } else if cfg!(target_os = "windows") {
		WIN_CLANG_DIRS.iter().map(ToString::to_string).collect()
    } else {
        panic!("Platform not supported");
    };

    let clang_lib = if cfg!(target_os = "windows") {
			format!("libclang{}", env::consts::DLL_SUFFIX)
		} else {
			format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)
		};

    let maybe_clang_dir = possible_clang_dirs.iter().filter_map(|candidate_dir| {
        let clang_dir = Path::new(candidate_dir);
        let clang_path = clang_dir.join(clang_lib.clone());
        if path_exists(&*clang_path) {
            Some(clang_dir)
        } else {
            None
        }
    }).next();

    macro_rules! qw {
        ($($i:ident)*) => (vec!($(stringify!($i)),*));
    }

    if let Some(clang_dir) = maybe_clang_dir {
        if use_static_lib {
            let libs = qw![
                LLVMAnalysis
                LLVMBitReader
                LLVMCore
                LLVMLTO
                LLVMLinker
                LLVMMC
                LLVMMCParser
                LLVMObjCARCOpts
                LLVMObject
                LLVMOption
                LLVMScalarOpts
                LLVMSupport
                LLVMTarget
                LLVMTransformUtils
                LLVMVectorize
                LLVMipa
                LLVMipo
                clang
                clangARCMigrate
                clangAST
                clangASTMatchers
                clangAnalysis
                clangBasic
                clangDriver
                clangEdit
                clangFormat
                clangFrontend
                clangIndex
                clangLex
                clangParse
                clangRewrite
                clangRewriteFrontend
                clangSema
                clangSerialization
                clangStaticAnalyzerCheckers
                clangStaticAnalyzerCore
                clangStaticAnalyzerFrontend
                clangTooling
            ];

            print!("cargo:rustc-flags=");
            for lib in libs {
                print!("-l static={} ", lib);
            }
            println!("-L {} -l ncursesw -l z -l stdc++", clang_dir.to_str().unwrap());
        } else{
            println!("cargo:rustc-link-search=native={}", clang_dir.to_str().unwrap());
            println!("cargo:rustc-link-lib=dylib=clang");
        }
    } else {
        panic!("Unable to find {}", clang_lib);
    }
}
<|endoftext|>


<commit_before>extern crate amq_protocol_codegen;
extern crate handlebars;
extern crate serde_json;

use amq_protocol_codegen::*;
use handlebars::{Handlebars,Helper,HelperDef,RenderContext,RenderError};

use std::collections::BTreeMap;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::ascii::AsciiExt;

fn main() {
    let out_dir      = env::var("OUT_DIR").expect("OUT_DIR is not defined");
    let dest_path    = Path::new(&out_dir).join("generated.rs");
    let mut f        = File::create(&dest_path).expect("Failed to create generated.rs");
    let mut main_tpl = String::new();
    let mut full_tpl = String::new();
    std::fs::File::open("templates/main.rs").expect("Failed to open main template").read_to_string(&mut full_tpl).expect("Failed to read main template");

    let mut handlebars = Handlebars::new();
    let mut data = BTreeMap::new();

    handlebars.register_escape_fn(handlebars::no_escape);
    handlebars.register_helper("snake", Box::new(snake_helper));
    handlebars.register_helper("camel", Box::new(camel_helper));
    handlebars.register_helper("map_type", Box::new(map_type_helper));
    handlebars.register_helper("map_parser", Box::new(map_parser_helper));

    handlebars.register_template_string("full", full_tpl).expect("Failed to register full template");

    let specs     = AMQProtocolDefinition::load();
    data.insert("specs".to_string(), specs);
    writeln!(f, "{}", handlebars.render("full", &data).expect("Failed to render full template"));

    //writeln!(f, "{}", specs.codegen_full(&full_tpl)).expect("Failed to generate generated.rs");
}

fn camel_name(name: &str) -> String {
    let mut new_word: bool = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

fn snake_name(name: &str) -> String {
    match name {
        "type"   => "amqp_type".to_string(),
        "return" => "amqp_return".to_string(),
        name     => name.replace("-", "_"),
    }
}

fn camel_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    // just for example, add error check for unwrap
    let param = h.param(0).unwrap().value().as_str().unwrap();
    let rendered = camel_name(param);
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

fn snake_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    // just for example, add error check for unwrap
    let param = h.param(0).unwrap().value().as_str().unwrap();
    let rendered = snake_name(param);
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

fn map_type_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
  println!("val1: {:?}", h.param(0));
  println!("val2: {:?}", h.param(1));
  let val = h.param(0).unwrap().value().clone();
  let arg:AMQPArgument = serde_json::from_value(val).unwrap();

  let rendered = match arg.amqp_type {
    Some(AMQPType::Bit)       => "bool",
    Some(AMQPType::Octet)     => "u8",
    Some(AMQPType::Short)     => "u16",
    Some(AMQPType::Long)      => "u32",
    Some(AMQPType::LongLong)  => "u64",
    Some(AMQPType::ShortStr)  => "String",
    Some(AMQPType::LongStr)   => "String",
    Some(AMQPType::Table)     => "::std::collections::HashMap<String,Value>",
    Some(AMQPType::Timestamp) => "u64",
    None                      => "()",
  };
  try!(rc.writer.write(rendered.as_bytes()));

  Ok(())
}

fn map_parser_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
  println!("val1: {:?}", h.param(0));
  let val = h.param(0).unwrap().value().clone();
  let arg:AMQPArgument = serde_json::from_value(val).unwrap();

  let rendered = match arg.amqp_type {
    Some(AMQPType::Bit)       => "map!(be_u8, |u| u != 0)",
    Some(AMQPType::Octet)     => "be_u8",
    Some(AMQPType::Short)     => "be_u16",
    Some(AMQPType::Long)      => "be_u32",
    Some(AMQPType::LongLong)  => "be_u64",
    Some(AMQPType::ShortStr)  => "map!(short_string, |s:&str| s.to_string())",
    Some(AMQPType::LongStr)   => "map!(long_string,  |s:&str| s.to_string())",
    Some(AMQPType::Table)     => "field_table",
    Some(AMQPType::Timestamp) => "be_u64",
    None                      => "value!(())",
  };
  try!(rc.writer.write(rendered.as_bytes()));

  Ok(())
}
<commit_msg>generate types and parsers for domains<commit_after>extern crate amq_protocol_codegen;
extern crate handlebars;
extern crate serde_json;

use amq_protocol_codegen::*;
use handlebars::{Handlebars,Helper,HelperDef,RenderContext,RenderError};

use std::collections::BTreeMap;
use std::env;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::ascii::AsciiExt;

fn main() {
    let out_dir      = env::var("OUT_DIR").expect("OUT_DIR is not defined");
    let dest_path    = Path::new(&out_dir).join("generated.rs");
    let mut f        = File::create(&dest_path).expect("Failed to create generated.rs");
    let mut main_tpl = String::new();
    let mut full_tpl = String::new();
    std::fs::File::open("templates/main.rs").expect("Failed to open main template").read_to_string(&mut full_tpl).expect("Failed to read main template");

    let mut handlebars = Handlebars::new();
    let mut data = BTreeMap::new();

    handlebars.register_escape_fn(handlebars::no_escape);
    handlebars.register_helper("snake", Box::new(snake_helper));
    handlebars.register_helper("camel", Box::new(camel_helper));
    handlebars.register_helper("map_type", Box::new(map_type_helper));
    handlebars.register_helper("map_parser", Box::new(map_parser_helper));

    handlebars.register_template_string("full", full_tpl).expect("Failed to register full template");

    let specs     = AMQProtocolDefinition::load();
    data.insert("specs".to_string(), specs);
    writeln!(f, "{}", handlebars.render("full", &data).expect("Failed to render full template"));

    //writeln!(f, "{}", specs.codegen_full(&full_tpl)).expect("Failed to generate generated.rs");
}

fn camel_name(name: &str) -> String {
    let mut new_word: bool = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

fn snake_name(name: &str) -> String {
    match name {
        "type"   => "amqp_type".to_string(),
        "return" => "amqp_return".to_string(),
        name     => name.replace("-", "_"),
    }
}

fn camel_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    // just for example, add error check for unwrap
    let param = h.param(0).unwrap().value().as_str().unwrap();
    let rendered = camel_name(param);
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

fn snake_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    // just for example, add error check for unwrap
    let param = h.param(0).unwrap().value().as_str().unwrap();
    let rendered = snake_name(param);
    try!(rc.writer.write(rendered.into_bytes().as_ref()));
    Ok(())
}

fn map_type_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
  println!("val1: {:?}", h.param(0));
  let val = h.param(0).unwrap().value().clone();
  let arg:AMQPArgument = serde_json::from_value(val.clone()).unwrap();

  let rendered = match arg.amqp_type {
    Some(AMQPType::Bit)       => "bool",
    Some(AMQPType::Octet)     => "u8",
    Some(AMQPType::Short)     => "u16",
    Some(AMQPType::Long)      => "u32",
    Some(AMQPType::LongLong)  => "u64",
    Some(AMQPType::ShortStr)  => "String",
    Some(AMQPType::LongStr)   => "String",
    Some(AMQPType::Table)     => "::std::collections::HashMap<String,Value>",
    Some(AMQPType::Timestamp) => "u64",
    None                      => {
      let data:  serde_json::Map<String,  serde_json::Value> = serde_json::from_value(rc.context().data().clone()).unwrap();
      let specs: serde_json::Map<String, serde_json::Value> =  serde_json::from_value(data.get("specs").unwrap().clone()).unwrap();
      let domains: Vec<AMQPDomain> =  serde_json::from_value(specs.get("domains").unwrap().clone()).unwrap();

      let lookup_domain = arg.domain.clone().unwrap();
      let res = domains.iter().find(|d| d.0 == lookup_domain).map(|d| match d.1 {
        AMQPType::Bit       => "bool",
        AMQPType::Octet     => "u8",
        AMQPType::Short     => "u16",
        AMQPType::Long      => "u32",
        AMQPType::LongLong  => "u64",
        AMQPType::ShortStr  => "String",
        AMQPType::LongStr   => "String",
        AMQPType::Table     => "::std::collections::HashMap<String,Value>",
        AMQPType::Timestamp => "u64",
      }).unwrap();
      println!("key {} => domain gave type: {}", arg.name, res);
      res
    },
  };
  try!(rc.writer.write(rendered.as_bytes()));

  Ok(())
}

fn map_parser_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
  println!("val1: {:?}", h.param(0));
  let val = h.param(0).unwrap().value().clone();
  let arg:AMQPArgument = serde_json::from_value(val).unwrap();

  let rendered = match arg.amqp_type {
    Some(AMQPType::Bit)       => "map!(be_u8, |u| u != 0)",
    Some(AMQPType::Octet)     => "be_u8",
    Some(AMQPType::Short)     => "be_u16",
    Some(AMQPType::Long)      => "be_u32",
    Some(AMQPType::LongLong)  => "be_u64",
    Some(AMQPType::ShortStr)  => "map!(short_string, |s:&str| s.to_string())",
    Some(AMQPType::LongStr)   => "map!(long_string,  |s:&str| s.to_string())",
    Some(AMQPType::Table)     => "field_table",
    Some(AMQPType::Timestamp) => "be_u64",
    None                      => {
      let data:  serde_json::Map<String,  serde_json::Value> = serde_json::from_value(rc.context().data().clone()).unwrap();
      let specs: serde_json::Map<String, serde_json::Value> =  serde_json::from_value(data.get("specs").unwrap().clone()).unwrap();
      let domains: Vec<AMQPDomain> =  serde_json::from_value(specs.get("domains").unwrap().clone()).unwrap();

      let lookup_domain = arg.domain.clone().unwrap();
      domains.iter().find(|d| d.0 == lookup_domain).map(|d| match d.1 {
        AMQPType::Bit       => "map!(be_u8, |u| u != 0)",
        AMQPType::Octet     => "be_u8",
        AMQPType::Short     => "be_u16",
        AMQPType::Long      => "be_u32",
        AMQPType::LongLong  => "be_u64",
        AMQPType::ShortStr  => "map!(short_string, |s:&str| s.to_string())",
        AMQPType::LongStr   => "map!(long_string,  |s:&str| s.to_string())",
        AMQPType::Table     => "field_table",
        AMQPType::Timestamp => "be_u64",
      }).unwrap()
    }
  };
  try!(rc.writer.write(rendered.as_bytes()));

  Ok(())
}
<|endoftext|>


<commit_before>use std::cell::{Cell, Ref, RefCell};
use std::fmt::Write as FmtWrite;
use std::io::SeekFrom;
use std::io::prelude::*;
use std::mem;
use std::path::Path;
use std::str;

use git2;
use hex;
use serde_json;
use lazycell::LazyCell;

use core::{PackageId, SourceId};
use sources::git;
use sources::registry::{RegistryConfig, RegistryData, CRATE_TEMPLATE, INDEX_LOCK, VERSION_TEMPLATE};
use util::network;
use util::{FileLock, Filesystem};
use util::{Config, Progress, Sha256, ToUrl};
use util::errors::{CargoResult, CargoResultExt, HttpNot200};

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.index_path.clone().into_path_unlocked();

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            let lock =
                self.index_path
                    .open_rw(Path::new(INDEX_LOCK), self.config, "the registry index")?;
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    let _ = lock.remove_siblings();

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).chain_err(|| {
                        "failed to initialized index git repository"
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let oid = self.repo()?.refname_to_id("refs/remotes/origin/master")?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<git2::Tree>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn load(
        &self,
        _root: &Path,
        path: &Path,
        data: &mut FnMut(&[u8]) -> CargoResult<()>,
    ) -> CargoResult<()> {
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        let repo = self.repo()?;
        let tree = self.tree()?;
        let entry = tree.get_path(path)?;
        let object = entry.to_object(repo)?;
        let blob = match object.as_blob() {
            Some(blob) => blob,
            None => bail!("path `{}` is not a blob in the git repo", path.display()),
        };
        data(blob.content())
    }

    fn config(&mut self) -> CargoResult<Option<RegistryConfig>> {
        debug!("loading config");
        self.prepare()?;
        let _lock =
            self.index_path
                .open_ro(Path::new(INDEX_LOCK), self.config, "the registry index")?;
        let mut config = None;
        self.load(Path::new(""), Path::new("config.json"), &mut |json| {
            config = Some(serde_json::from_slice(json)?);
            Ok(())
        })?;
        trace!("config loaded");
        Ok(config)
    }

    fn update_index(&mut self) -> CargoResult<()> {
        if self.config.cli_unstable().offline {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        let _lock =
            self.index_path
                .open_rw(Path::new(INDEX_LOCK), self.config, "the registry index")?;
        self.config
            .shell()
            .status("Updating", self.source_id.display_registry())?;

        // git fetch origin master
        let url = self.source_id.url();
        let refspec = "refs/heads/master:refs/remotes/origin/master";
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url, refspec, self.config)
            .chain_err(|| format!("failed to fetch `{}`", url))?;
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str) -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        // Attempt to open an read-only copy first to avoid an exclusive write
        // lock and also work with read-only filesystems. Note that we check the
        // length of the file like below to handle interrupted downloads.
        //
        // If this fails then we fall through to the exclusive path where we may
        // have to redownload the file.
        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            let meta = dst.file().metadata()?;
            if meta.len() > 0 {
                return Ok(dst);
            }
        }
        let mut dst = self.cache_path.open_rw(path, self.config, &filename)?;
        let meta = dst.file().metadata()?;
        if meta.len() > 0 {
            return Ok(dst);
        }
        self.config.shell().status("Downloading", pkg)?;

        let config = self.config()?.unwrap();
        let mut url = config.dl.clone();
        if !url.contains(CRATE_TEMPLATE) && !url.contains(VERSION_TEMPLATE) {
            write!(url, "/{}/{}/download", CRATE_TEMPLATE, VERSION_TEMPLATE).unwrap();
        }
        let url = url.replace(CRATE_TEMPLATE, &*pkg.name())
            .replace(VERSION_TEMPLATE, &pkg.version().to_string())
            .to_url()?;

        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        let url = url.to_string();
        let mut handle = self.config.http()?.borrow_mut();
        handle.get(true)?;
        handle.url(&url)?;
        handle.follow_location(true)?;
        let mut state = Sha256::new();
        let mut body = Vec::new();
        network::with_retry(self.config, || {
            state = Sha256::new();
            body = Vec::new();
            let mut pb = Progress::new("Fetch", self.config);
            {
                handle.progress(true)?;
                let mut handle = handle.transfer();
                handle.progress_function(|dl_total, dl_cur, _, _| {
                    pb.tick(dl_cur as usize, dl_total as usize).is_ok()
                })?;
                handle.write_function(|buf| {
                    state.update(buf);
                    body.extend_from_slice(buf);
                    Ok(buf.len())
                })?;
                handle.perform()?;
            }
            let code = handle.response_code()?;
            if code != 200 && code != 0 {
                let url = handle.effective_url()?.unwrap_or(&url);
                Err(HttpNot200 {
                    code,
                    url: url.to_string(),
                }.into())
            } else {
                Ok(())
            }
        })?;

        // Verify what we just downloaded
        if hex::encode(state.finish()) != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        dst.write_all(&body)?;
        dst.seek(SeekFrom::Start(0))?;
        Ok(dst)
    }

    fn is_crate_downloaded(&self, pkg: &PackageId) -> bool {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            if let Ok(meta) = dst.file().metadata() {
                return meta.len() > 0;
            }
        }
        false
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<commit_msg>Auto merge of #5926 - alexcrichton:more-errors, r=dwijnand<commit_after>use std::cell::{Cell, Ref, RefCell};
use std::fmt::Write as FmtWrite;
use std::io::SeekFrom;
use std::io::prelude::*;
use std::mem;
use std::path::Path;
use std::str;

use git2;
use hex;
use serde_json;
use lazycell::LazyCell;

use core::{PackageId, SourceId};
use sources::git;
use sources::registry::{RegistryConfig, RegistryData, CRATE_TEMPLATE, INDEX_LOCK, VERSION_TEMPLATE};
use util::network;
use util::{FileLock, Filesystem};
use util::{Config, Progress, Sha256, ToUrl};
use util::errors::{CargoResult, CargoResultExt, HttpNot200};

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    tree: RefCell<Option<git2::Tree<'static>>>,
    repo: LazyCell<git2::Repository>,
    head: Cell<Option<git2::Oid>>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config,
            tree: RefCell::new(None),
            repo: LazyCell::new(),
            head: Cell::new(None),
        }
    }

    fn repo(&self) -> CargoResult<&git2::Repository> {
        self.repo.try_borrow_with(|| {
            let path = self.index_path.clone().into_path_unlocked();

            // Fast path without a lock
            if let Ok(repo) = git2::Repository::open(&path) {
                trace!("opened a repo without a lock");
                return Ok(repo);
            }

            // Ok, now we need to lock and try the whole thing over again.
            trace!("acquiring registry index lock");
            let lock =
                self.index_path
                    .open_rw(Path::new(INDEX_LOCK), self.config, "the registry index")?;
            match git2::Repository::open(&path) {
                Ok(repo) => Ok(repo),
                Err(_) => {
                    let _ = lock.remove_siblings();

                    // Note that we'd actually prefer to use a bare repository
                    // here as we're not actually going to check anything out.
                    // All versions of Cargo, though, share the same CARGO_HOME,
                    // so for compatibility with older Cargo which *does* do
                    // checkouts we make sure to initialize a new full
                    // repository (not a bare one).
                    //
                    // We should change this to `init_bare` whenever we feel
                    // like enough time has passed or if we change the directory
                    // that the folder is located in, such as by changing the
                    // hash at the end of the directory.
                    //
                    // Note that in the meantime we also skip `init.templatedir`
                    // as it can be misconfigured sometimes or otherwise add
                    // things that we don't want.
                    let mut opts = git2::RepositoryInitOptions::new();
                    opts.external_template(false);
                    Ok(git2::Repository::init_opts(&path, &opts).chain_err(|| {
                        "failed to initialized index git repository"
                    })?)
                }
            }
        })
    }

    fn head(&self) -> CargoResult<git2::Oid> {
        if self.head.get().is_none() {
            let oid = self.repo()?.refname_to_id("refs/remotes/origin/master")?;
            self.head.set(Some(oid));
        }
        Ok(self.head.get().unwrap())
    }

    fn tree(&self) -> CargoResult<Ref<git2::Tree>> {
        {
            let tree = self.tree.borrow();
            if tree.is_some() {
                return Ok(Ref::map(tree, |s| s.as_ref().unwrap()));
            }
        }
        let repo = self.repo()?;
        let commit = repo.find_commit(self.head()?)?;
        let tree = commit.tree()?;

        // Unfortunately in libgit2 the tree objects look like they've got a
        // reference to the repository object which means that a tree cannot
        // outlive the repository that it came from. Here we want to cache this
        // tree, though, so to accomplish this we transmute it to a static
        // lifetime.
        //
        // Note that we don't actually hand out the static lifetime, instead we
        // only return a scoped one from this function. Additionally the repo
        // we loaded from (above) lives as long as this object
        // (`RemoteRegistry`) so we then just need to ensure that the tree is
        // destroyed first in the destructor, hence the destructor on
        // `RemoteRegistry` below.
        let tree = unsafe { mem::transmute::<git2::Tree, git2::Tree<'static>>(tree) };
        *self.tree.borrow_mut() = Some(tree);
        Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap()))
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn prepare(&self) -> CargoResult<()> {
        self.repo()?; // create intermediate dirs and initialize the repo
        Ok(())
    }

    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn load(
        &self,
        _root: &Path,
        path: &Path,
        data: &mut FnMut(&[u8]) -> CargoResult<()>,
    ) -> CargoResult<()> {
        // Note that the index calls this method and the filesystem is locked
        // in the index, so we don't need to worry about an `update_index`
        // happening in a different process.
        let repo = self.repo()?;
        let tree = self.tree()?;
        let entry = tree.get_path(path)?;
        let object = entry.to_object(repo)?;
        let blob = match object.as_blob() {
            Some(blob) => blob,
            None => bail!("path `{}` is not a blob in the git repo", path.display()),
        };
        data(blob.content())
    }

    fn config(&mut self) -> CargoResult<Option<RegistryConfig>> {
        debug!("loading config");
        self.prepare()?;
        let _lock =
            self.index_path
                .open_ro(Path::new(INDEX_LOCK), self.config, "the registry index")?;
        let mut config = None;
        self.load(Path::new(""), Path::new("config.json"), &mut |json| {
            config = Some(serde_json::from_slice(json)?);
            Ok(())
        })?;
        trace!("config loaded");
        Ok(config)
    }

    fn update_index(&mut self) -> CargoResult<()> {
        if self.config.cli_unstable().offline {
            return Ok(());
        }
        if self.config.cli_unstable().no_index_update {
            return Ok(());
        }

        debug!("updating the index");

        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        self.config.http()?;

        self.prepare()?;
        self.head.set(None);
        *self.tree.borrow_mut() = None;
        let _lock =
            self.index_path
                .open_rw(Path::new(INDEX_LOCK), self.config, "the registry index")?;
        self.config
            .shell()
            .status("Updating", self.source_id.display_registry())?;

        // git fetch origin master
        let url = self.source_id.url();
        let refspec = "refs/heads/master:refs/remotes/origin/master";
        let repo = self.repo.borrow_mut().unwrap();
        git::fetch(repo, url, refspec, self.config)
            .chain_err(|| format!("failed to fetch `{}`", url))?;
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str) -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        // Attempt to open an read-only copy first to avoid an exclusive write
        // lock and also work with read-only filesystems. Note that we check the
        // length of the file like below to handle interrupted downloads.
        //
        // If this fails then we fall through to the exclusive path where we may
        // have to redownload the file.
        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            let meta = dst.file().metadata()?;
            if meta.len() > 0 {
                return Ok(dst);
            }
        }
        let mut dst = self.cache_path.open_rw(path, self.config, &filename)?;
        let meta = dst.file().metadata()?;
        if meta.len() > 0 {
            return Ok(dst);
        }
        self.config.shell().status("Downloading", pkg)?;

        let config = self.config()?.unwrap();
        let mut url = config.dl.clone();
        if !url.contains(CRATE_TEMPLATE) && !url.contains(VERSION_TEMPLATE) {
            write!(url, "/{}/{}/download", CRATE_TEMPLATE, VERSION_TEMPLATE).unwrap();
        }
        let url = url.replace(CRATE_TEMPLATE, &*pkg.name())
            .replace(VERSION_TEMPLATE, &pkg.version().to_string())
            .to_url()?;

        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        let url = url.to_string();
        let mut handle = self.config.http()?.borrow_mut();
        handle.get(true)?;
        handle.url(&url)?;
        handle.follow_location(true)?;
        let mut state = Sha256::new();
        let mut body = Vec::new();
        network::with_retry(self.config, || {
            state = Sha256::new();
            body = Vec::new();
            let mut pb = Progress::new("Fetch", self.config);
            {
                handle.progress(true)?;
                let mut handle = handle.transfer();
                handle.progress_function(|dl_total, dl_cur, _, _| {
                    pb.tick(dl_cur as usize, dl_total as usize).is_ok()
                })?;
                handle.write_function(|buf| {
                    state.update(buf);
                    body.extend_from_slice(buf);
                    Ok(buf.len())
                })?;
                handle.perform().chain_err(|| {
                    format!("failed to download from `{}`", url)
                })?;
            }
            let code = handle.response_code()?;
            if code != 200 && code != 0 {
                let url = handle.effective_url()?.unwrap_or(&url);
                Err(HttpNot200 {
                    code,
                    url: url.to_string(),
                }.into())
            } else {
                Ok(())
            }
        })?;

        // Verify what we just downloaded
        if hex::encode(state.finish()) != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        dst.write_all(&body)?;
        dst.seek(SeekFrom::Start(0))?;
        Ok(dst)
    }

    fn is_crate_downloaded(&self, pkg: &PackageId) -> bool {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);

        if let Ok(dst) = self.cache_path.open_ro(path, self.config, &filename) {
            if let Ok(meta) = dst.file().metadata() {
                return meta.len() > 0;
            }
        }
        false
    }
}

impl<'cfg> Drop for RemoteRegistry<'cfg> {
    fn drop(&mut self) {
        // Just be sure to drop this before our other fields
        self.tree.borrow_mut().take();
    }
}
<|endoftext|>


<commit_before>// TODO: used by grpc-rust, should move it into separate crate.
#![doc(hidden)]

use std::io::Write;

use crate::gen::rust_name::RustRelativePath;

/// Field visibility.
pub(crate) enum Visibility {
    Public,
    Default,
    Path(RustRelativePath),
}

pub(crate) struct CodeWriter<'a> {
    writer: &'a mut (dyn Write + 'a),
    indent: String,
}

impl<'a> CodeWriter<'a> {
    pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
        CodeWriter {
            writer: writer,
            indent: "".to_string(),
        }
    }

    pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
        (if line.as_ref().is_empty() {
            self.writer.write_all("\n".as_bytes())
        } else {
            let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
            self.writer.write_all(s.as_bytes())
        })
        .unwrap();
    }

    pub fn write_generated_by(&mut self, pkg: &str, version: &str, parser: &str) {
        self.write_line(format!(
            "// This file is generated by {pkg} {version}. Do not edit",
            pkg = pkg,
            version = version
        ));
        self.write_line(format!(
            "// .proto file is parsed by {parser}",
            parser = parser
        ));
        self.write_generated_common();
    }

    fn write_generated_common(&mut self) {
        // https://secure.phabricator.com/T784
        self.write_line("// @generated");

        self.write_line("");
        self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
        self.write_line("#![allow(unknown_lints)]");
        self.write_line("#![allow(clippy::all)]");
        self.write_line("");
        self.write_line("#![allow(unused_attributes)]");
        self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
        self.write_line("");
        self.write_line("#![allow(box_pointers)]");
        self.write_line("#![allow(dead_code)]");
        self.write_line("#![allow(missing_docs)]");
        self.write_line("#![allow(non_camel_case_types)]");
        self.write_line("#![allow(non_snake_case)]");
        self.write_line("#![allow(non_upper_case_globals)]");
        self.write_line("#![allow(trivial_casts)]");
        self.write_line("#![allow(unused_results)]");
        self.write_line("#![allow(unused_mut)]");
    }

    pub fn unimplemented(&mut self) {
        self.write_line(format!("unimplemented!();"));
    }

    pub fn indented<F>(&mut self, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        cb(&mut CodeWriter {
            writer: self.writer,
            indent: format!("{}    ", self.indent),
        });
    }

    #[allow(dead_code)]
    pub fn commented<F>(&mut self, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        cb(&mut CodeWriter {
            writer: self.writer,
            indent: format!("// {}", self.indent),
        });
    }

    pub fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
        self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
    }

    pub fn lazy_static(&mut self, name: &str, ty: &str, protobuf_crate_path: &str) {
        self.write_line(&format!(
            "static {}: {}::rt::LazyV2<{}> = {}::rt::LazyV2::INIT;",
            name, protobuf_crate_path, ty, protobuf_crate_path,
        ));
    }

    pub fn lazy_static_decl_get_simple(
        &mut self,
        name: &str,
        ty: &str,
        init: &str,
        protobuf_crate_path: &str,
    ) {
        self.lazy_static(name, ty, protobuf_crate_path);
        self.write_line(&format!("{}.get({})", name, init));
    }

    pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.write_line(first_line);
        self.indented(cb);
        self.write_line(last_line);
    }

    pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.block(&format!("{} {{", prefix), "}", cb);
    }

    pub fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
    }

    pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("impl {}", name.as_ref()), cb);
    }

    pub fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, tr: S1, ty: S2, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
    }

    pub fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        let args_str = if args.is_empty() {
            "".to_owned()
        } else {
            format!("<{}>", args.join(", "))
        };
        self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
    }

    pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
    }

    pub fn pub_enum<F>(&mut self, name: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub enum {}", name), cb);
    }

    pub fn field_entry(&mut self, name: &str, value: &str) {
        self.write_line(&format!("{}: {},", name, value));
    }

    pub fn field_decl(&mut self, name: &str, field_type: &str) {
        self.write_line(&format!("{}: {},", name, field_type));
    }

    pub fn pub_field_decl(&mut self, name: &str, field_type: &str) {
        self.write_line(&format!("pub {}: {},", name, field_type));
    }

    pub(crate) fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
        match vis {
            Visibility::Public => self.pub_field_decl(name, field_type),
            Visibility::Default => self.field_decl(name, field_type),
            Visibility::Path(..) => unimplemented!(),
        }
    }

    pub fn derive(&mut self, derive: &[&str]) {
        let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
        self.write_line(&format!("#[derive({})]", v.join(",")));
    }

    pub fn allow(&mut self, what: &[&str]) {
        let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
        self.write_line(&format!("#[allow({})]", v.join(",")));
    }

    pub fn comment(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("//");
        } else {
            self.write_line(&format!("// {}", comment));
        }
    }

    fn documentation(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("///");
        } else {
            self.write_line(&format!("/// {}", comment));
        }
    }

    pub fn mod_doc(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("//!");
        } else {
            self.write_line(&format!("//! {}", comment));
        }
    }

    /// Writes the documentation of the given path.
    ///
    /// Protobuf paths are defined in proto/google/protobuf/descriptor.proto,
    /// in the `SourceCodeInfo` message.
    ///
    /// For example, say we have a file like:
    ///
    /// ```ignore
    /// message Foo {
    ///   optional string foo = 1;
    /// }
    /// ```
    ///
    /// Let's look at just the field definition. We have the following paths:
    ///
    /// ```ignore
    /// path               represents
    /// [ 4, 0, 2, 0 ]     The whole field definition.
    /// [ 4, 0, 2, 0, 4 ]  The label (optional).
    /// [ 4, 0, 2, 0, 5 ]  The type (string).
    /// [ 4, 0, 2, 0, 1 ]  The name (foo).
    /// [ 4, 0, 2, 0, 3 ]  The number (1).
    /// ```
    ///
    /// The `4`s can be obtained using simple introspection:
    ///
    /// ```
    /// use protobuf::descriptor::FileDescriptorProto;
    /// use protobuf::reflect::MessageDescriptor;
    ///
    /// let id = MessageDescriptor::for_type::<FileDescriptorProto>()
    ///     .get_field_by_name("message_type")
    ///     .expect("`message_type` must exist")
    ///     .get_proto()
    ///     .get_number();
    ///
    /// assert_eq!(id, 4);
    /// ```
    ///
    /// The first `0` here means this path refers to the first message.
    ///
    /// The `2` then refers to the `field` field on the `DescriptorProto` message.
    ///
    /// Then comes another `0` to refer to the first field of the current message.
    ///
    /// Etc.

    pub fn all_documentation(
        &mut self,
        info: Option<&protobuf::descriptor::SourceCodeInfo>,
        path: &[i32],
    ) {
        let doc = info
            .map(|v| &v.location)
            .and_then(|ls| ls.iter().find(|l| l.path == path))
            .map(|l| l.get_leading_comments());

        let lines = doc
            .iter()
            .map(|doc| doc.lines())
            .flatten()
            .collect::<Vec<_>>();

        // Skip comments with code blocks to avoid rustdoc trying to compile them.
        if !lines.iter().any(|line| line.starts_with("    ")) {
            for doc in &lines {
                self.documentation(doc);
            }
        }
    }

    pub(crate) fn fn_block<F>(&mut self, vis: Visibility, sig: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        match vis {
            Visibility::Public => self.expr_block(&format!("pub fn {}", sig), cb),
            Visibility::Default => self.expr_block(&format!("fn {}", sig), cb),
            Visibility::Path(p) if p.is_empty() => self.expr_block(&format!("fn {}", sig), cb),
            Visibility::Path(p) => self.expr_block(&format!("pub(in {}) fn {}", p, sig), cb),
        }
    }

    pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.fn_block(Visibility::Public, sig, cb);
    }

    pub fn def_fn<F>(&mut self, sig: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.fn_block(Visibility::Default, sig, cb);
    }

    pub fn pub_mod<F>(&mut self, name: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub mod {}", name), cb)
    }

    pub fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("while {}", cond.as_ref()), cb);
    }

    // if ... { ... }
    pub fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.expr_block(&format!("if {}", cond.as_ref()), cb);
    }

    // if ... {} else { ... }
    pub fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.write_line(&format!("if {} {{", cond.as_ref()));
        self.write_line("} else {");
        self.indented(cb);
        self.write_line("}");
    }

    // if let ... = ... { ... }
    pub fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.if_stmt(&format!("let {} = {}", decl, expr), cb);
    }

    // if let ... = ... { } else { ... }
    pub fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
    }

    pub fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
    }

    pub fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.stmt_block(&format!("match {}", value.as_ref()), cb);
    }

    pub fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("match {}", value.as_ref()), cb);
    }

    pub fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
    }

    pub fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
        self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
    }
}
<commit_msg>Properly write at-generated<commit_after>// TODO: used by grpc-rust, should move it into separate crate.
#![doc(hidden)]

use std::io::Write;

use crate::gen::rust_name::RustRelativePath;

/// Field visibility.
pub(crate) enum Visibility {
    Public,
    Default,
    Path(RustRelativePath),
}

pub(crate) struct CodeWriter<'a> {
    writer: &'a mut (dyn Write + 'a),
    indent: String,
}

impl<'a> CodeWriter<'a> {
    pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
        CodeWriter {
            writer: writer,
            indent: "".to_string(),
        }
    }

    pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
        (if line.as_ref().is_empty() {
            self.writer.write_all("\n".as_bytes())
        } else {
            let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
            self.writer.write_all(s.as_bytes())
        })
        .unwrap();
    }

    pub fn write_generated_by(&mut self, pkg: &str, version: &str, parser: &str) {
        self.write_line(format!(
            "// This file is generated by {pkg} {version}. Do not edit",
            pkg = pkg,
            version = version
        ));
        self.write_line(format!(
            "// .proto file is parsed by {parser}",
            parser = parser
        ));
        self.write_generated_common();
    }

    fn write_generated_common(&mut self) {
        // https://secure.phabricator.com/T784
        self.write_line(&format!("// {}generated", "@"));

        self.write_line("");
        self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
        self.write_line("#![allow(unknown_lints)]");
        self.write_line("#![allow(clippy::all)]");
        self.write_line("");
        self.write_line("#![allow(unused_attributes)]");
        self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
        self.write_line("");
        self.write_line("#![allow(box_pointers)]");
        self.write_line("#![allow(dead_code)]");
        self.write_line("#![allow(missing_docs)]");
        self.write_line("#![allow(non_camel_case_types)]");
        self.write_line("#![allow(non_snake_case)]");
        self.write_line("#![allow(non_upper_case_globals)]");
        self.write_line("#![allow(trivial_casts)]");
        self.write_line("#![allow(unused_results)]");
        self.write_line("#![allow(unused_mut)]");
    }

    pub fn unimplemented(&mut self) {
        self.write_line(format!("unimplemented!();"));
    }

    pub fn indented<F>(&mut self, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        cb(&mut CodeWriter {
            writer: self.writer,
            indent: format!("{}    ", self.indent),
        });
    }

    #[allow(dead_code)]
    pub fn commented<F>(&mut self, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        cb(&mut CodeWriter {
            writer: self.writer,
            indent: format!("// {}", self.indent),
        });
    }

    pub fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
        self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
    }

    pub fn lazy_static(&mut self, name: &str, ty: &str, protobuf_crate_path: &str) {
        self.write_line(&format!(
            "static {}: {}::rt::LazyV2<{}> = {}::rt::LazyV2::INIT;",
            name, protobuf_crate_path, ty, protobuf_crate_path,
        ));
    }

    pub fn lazy_static_decl_get_simple(
        &mut self,
        name: &str,
        ty: &str,
        init: &str,
        protobuf_crate_path: &str,
    ) {
        self.lazy_static(name, ty, protobuf_crate_path);
        self.write_line(&format!("{}.get({})", name, init));
    }

    pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.write_line(first_line);
        self.indented(cb);
        self.write_line(last_line);
    }

    pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.block(&format!("{} {{", prefix), "}", cb);
    }

    pub fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
    }

    pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("impl {}", name.as_ref()), cb);
    }

    pub fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, tr: S1, ty: S2, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
    }

    pub fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        let args_str = if args.is_empty() {
            "".to_owned()
        } else {
            format!("<{}>", args.join(", "))
        };
        self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
    }

    pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
    }

    pub fn pub_enum<F>(&mut self, name: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub enum {}", name), cb);
    }

    pub fn field_entry(&mut self, name: &str, value: &str) {
        self.write_line(&format!("{}: {},", name, value));
    }

    pub fn field_decl(&mut self, name: &str, field_type: &str) {
        self.write_line(&format!("{}: {},", name, field_type));
    }

    pub fn pub_field_decl(&mut self, name: &str, field_type: &str) {
        self.write_line(&format!("pub {}: {},", name, field_type));
    }

    pub(crate) fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
        match vis {
            Visibility::Public => self.pub_field_decl(name, field_type),
            Visibility::Default => self.field_decl(name, field_type),
            Visibility::Path(..) => unimplemented!(),
        }
    }

    pub fn derive(&mut self, derive: &[&str]) {
        let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
        self.write_line(&format!("#[derive({})]", v.join(",")));
    }

    pub fn allow(&mut self, what: &[&str]) {
        let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
        self.write_line(&format!("#[allow({})]", v.join(",")));
    }

    pub fn comment(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("//");
        } else {
            self.write_line(&format!("// {}", comment));
        }
    }

    fn documentation(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("///");
        } else {
            self.write_line(&format!("/// {}", comment));
        }
    }

    pub fn mod_doc(&mut self, comment: &str) {
        if comment.is_empty() {
            self.write_line("//!");
        } else {
            self.write_line(&format!("//! {}", comment));
        }
    }

    /// Writes the documentation of the given path.
    ///
    /// Protobuf paths are defined in proto/google/protobuf/descriptor.proto,
    /// in the `SourceCodeInfo` message.
    ///
    /// For example, say we have a file like:
    ///
    /// ```ignore
    /// message Foo {
    ///   optional string foo = 1;
    /// }
    /// ```
    ///
    /// Let's look at just the field definition. We have the following paths:
    ///
    /// ```ignore
    /// path               represents
    /// [ 4, 0, 2, 0 ]     The whole field definition.
    /// [ 4, 0, 2, 0, 4 ]  The label (optional).
    /// [ 4, 0, 2, 0, 5 ]  The type (string).
    /// [ 4, 0, 2, 0, 1 ]  The name (foo).
    /// [ 4, 0, 2, 0, 3 ]  The number (1).
    /// ```
    ///
    /// The `4`s can be obtained using simple introspection:
    ///
    /// ```
    /// use protobuf::descriptor::FileDescriptorProto;
    /// use protobuf::reflect::MessageDescriptor;
    ///
    /// let id = MessageDescriptor::for_type::<FileDescriptorProto>()
    ///     .get_field_by_name("message_type")
    ///     .expect("`message_type` must exist")
    ///     .get_proto()
    ///     .get_number();
    ///
    /// assert_eq!(id, 4);
    /// ```
    ///
    /// The first `0` here means this path refers to the first message.
    ///
    /// The `2` then refers to the `field` field on the `DescriptorProto` message.
    ///
    /// Then comes another `0` to refer to the first field of the current message.
    ///
    /// Etc.

    pub fn all_documentation(
        &mut self,
        info: Option<&protobuf::descriptor::SourceCodeInfo>,
        path: &[i32],
    ) {
        let doc = info
            .map(|v| &v.location)
            .and_then(|ls| ls.iter().find(|l| l.path == path))
            .map(|l| l.get_leading_comments());

        let lines = doc
            .iter()
            .map(|doc| doc.lines())
            .flatten()
            .collect::<Vec<_>>();

        // Skip comments with code blocks to avoid rustdoc trying to compile them.
        if !lines.iter().any(|line| line.starts_with("    ")) {
            for doc in &lines {
                self.documentation(doc);
            }
        }
    }

    pub(crate) fn fn_block<F>(&mut self, vis: Visibility, sig: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        match vis {
            Visibility::Public => self.expr_block(&format!("pub fn {}", sig), cb),
            Visibility::Default => self.expr_block(&format!("fn {}", sig), cb),
            Visibility::Path(p) if p.is_empty() => self.expr_block(&format!("fn {}", sig), cb),
            Visibility::Path(p) => self.expr_block(&format!("pub(in {}) fn {}", p, sig), cb),
        }
    }

    pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.fn_block(Visibility::Public, sig, cb);
    }

    pub fn def_fn<F>(&mut self, sig: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.fn_block(Visibility::Default, sig, cb);
    }

    pub fn pub_mod<F>(&mut self, name: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("pub mod {}", name), cb)
    }

    pub fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("while {}", cond.as_ref()), cb);
    }

    // if ... { ... }
    pub fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.expr_block(&format!("if {}", cond.as_ref()), cb);
    }

    // if ... {} else { ... }
    pub fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.write_line(&format!("if {} {{", cond.as_ref()));
        self.write_line("} else {");
        self.indented(cb);
        self.write_line("}");
    }

    // if let ... = ... { ... }
    pub fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.if_stmt(&format!("let {} = {}", decl, expr), cb);
    }

    // if let ... = ... { } else { ... }
    pub fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
    }

    pub fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
    }

    pub fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
    where
        F: FnOnce(&mut CodeWriter),
    {
        self.stmt_block(&format!("match {}", value.as_ref()), cb);
    }

    pub fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.expr_block(&format!("match {}", value.as_ref()), cb);
    }

    pub fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
    where
        F: Fn(&mut CodeWriter),
    {
        self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
    }

    pub fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
        self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
    }
}
<|endoftext|>


<commit_before>extern crate csv;
extern crate rustc_serialize;

use std::collections::HashMap;

fn adjusted_cosine(vec_feat_1: Vec<f32>, vec_feat_2: Vec<f32>, vec_avg: Vec<f32>) -> f32 {
    if vec_feat_1.len() != vec_feat_2.len() || vec_feat_1.len() != vec_avg.len() {
        panic!("Should compare vectors of same size");
    }

    let n_users = vec_feat_1.len();

    let mut usr_pref = 0.0;
    let mut feat_1_sqr = 0.0;
    let mut feat_2_sqr = 0.0;

    for i in 0..n_users {
        if vec_feat_1[i] > 0.0 && vec_feat_2[i] > 0.0 {
            let feat_1_inf = vec_feat_1[i] - vec_avg[i];
            let feat_2_inf = vec_feat_2[i] - vec_avg[i];
            usr_pref += feat_1_inf * feat_2_inf;
            feat_1_sqr += feat_1_inf.powf(2.0);
            feat_2_sqr += feat_2_inf.powf(2.0);
        }
    }

    usr_pref / (feat_1_sqr.sqrt() * feat_2_sqr.sqrt())
}

fn get_user_vector(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                   name: &str)
                   -> Vec<f32> {
    let mut vec = Vec::new();
    let indx = records.1.get(name).unwrap();
    let i = records.0.len();

    for x in 0..i {
        vec.push(records.2[(*indx as usize * i + x) as usize]);
    }

    vec
}

fn get_feature_vector(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                      feat: &str)
                      -> Vec<f32> {
    let mut vec = Vec::new();
    let indx = records.0.get(feat).unwrap();
    let i = records.0.len();
    let j = records.1.len();

    for y in 0..j {
        vec.push(records.2[(y * i + *indx as usize) as usize]);
    }

    vec
}

fn get_user_avg(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>)) -> Vec<f32> {
    let mut vec = Vec::new();
    let i = records.0.len();
    let j = records.1.len();

    for y in 0..j {
        let mut rated = 0;
        let mut avg = 0.0;
        for x in 0..i {
            let rating = records.2[(y * i + x) as usize];
            if rating != 0.0 {
                avg += rating;
                rated += 1;
            }
        }
        avg /= rated as f32;
        vec.push(avg);
    }

    vec
}

fn normalize(vect: &Vec<f32>) -> Vec<f32> {
    let mut normd_vec = Vec::with_capacity(vect.len());

    let mut max = vect[0];
    let mut min = vect[0];

    for rating in vect.iter() {
        if *rating > max {
            max = *rating;
        }
        if *rating != 0.0 && *rating < min || min == 0.0 {
            min = *rating;
        }
    }

    print!("max: {}, min: {}\n", max, min);

    for rating in vect.iter() {
        if *rating > 0.0 {
            let norm = (2.0 * (rating - min) - (max - min)) / (max - min);
            normd_vec.push(norm);
        }
    }

    normd_vec
}

fn load_database(path: &str) -> (HashMap<String, u32>, HashMap<String, u32>, Vec<f32>) {
    let mut rdr = csv::Reader::from_file(path).unwrap().has_headers(true);
    let mut headers: HashMap<String, u32> = HashMap::new();
    let mut names: HashMap<String, u32> = HashMap::new();
    let mut records: Vec<f32> = Vec::new();

    let mut i = 0;
    let mut j = 0;

    loop {
        match rdr.next_bytes() {
            csv::NextField::Data(data) => {
                let d_string = String::from_utf8(data.to_vec()).unwrap();
                match j {
                    0 => {
                        /***********HEADERS***********/
                        if i > 0 {
                            headers.insert(d_string, i - 1);
                        }
                    }
                    _ => {
                        /***********RECORDS***********/
                        match i {
                            0 => {
                                /***********NAMES***********/
                                names.insert(d_string, j - 1);
                            }
                            _ => {
                                /***********VALUES***********/
                                records.push(d_string.parse::<f32>().unwrap());
                            }
                        }
                    }
                }
                i += 1;
            }
            csv::NextField::EndOfRecord => {
                j += 1;
                i = 0;
            }
            csv::NextField::EndOfCsv => break,
            csv::NextField::Error(err) => panic!(err),
        }
    }

    (headers, names, records)
}

fn item_similarity(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                   feat_1: &str,
                   feat_2: &str)
                   -> f32 {
    let v_feat_1 = get_feature_vector(&records, feat_1);
    let v_feat_2 = get_feature_vector(&records, feat_2);
    let v_avg = get_user_avg(&records);

    println!("AVG: {:?}", v_avg);

    adjusted_cosine(v_feat_1, v_feat_2, v_avg)
}

fn prediction(name: &str, feature: &str) -> f32 {
    // let records = load_database("./data/db1.csv");
    let records = load_database("./data/db1.1.csv");

    let user_vec = get_user_vector(&records, name);
    let normd_vec = normalize(&user_vec);
    let mut sim_vec = Vec::new();

    let mut i = 0;
    for (_feat, &_indx) in records.0.iter() {
        if _feat != feature {
            print!("{} - ", _feat);
            sim_vec.push(item_similarity(&records, _feat, feature));
        }
        i += 1;
    }

    println!("{:?}", normd_vec);
    println!("{:?}", sim_vec);

    3.1416
}

fn main() {
    println!("Prediction: {}", prediction("David", "Kacey Musgraves"));
}
<commit_msg>Finished<commit_after>extern crate csv;
extern crate rustc_serialize;

use std::collections::HashMap;

fn adjusted_cosine(vec_feat_1: Vec<f32>, vec_feat_2: Vec<f32>, vec_avg: Vec<f32>) -> f32 {
    if vec_feat_1.len() != vec_feat_2.len() || vec_feat_1.len() != vec_avg.len() {
        panic!("Should compare vectors of same size");
    }

    let n_users = vec_feat_1.len();

    let mut usr_pref = 0.0;
    let mut feat_1_sqr = 0.0;
    let mut feat_2_sqr = 0.0;

    for i in 0..n_users {
        if vec_feat_1[i] > 0.0 && vec_feat_2[i] > 0.0 {
            let feat_1_inf = vec_feat_1[i] - vec_avg[i];
            let feat_2_inf = vec_feat_2[i] - vec_avg[i];
            usr_pref += feat_1_inf * feat_2_inf;
            feat_1_sqr += feat_1_inf.powf(2.0);
            feat_2_sqr += feat_2_inf.powf(2.0);
        }
    }

    usr_pref / (feat_1_sqr.sqrt() * feat_2_sqr.sqrt())
}

fn get_user_vector(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                   name: &str)
                   -> Vec<f32> {
    let mut vec = Vec::new();
    let indx = records.1.get(name).unwrap();
    let i = records.0.len();

    for x in 0..i {
        vec.push(records.2[(*indx as usize * i + x) as usize]);
    }

    vec
}

fn get_feature_vector(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                      feat: &str)
                      -> Vec<f32> {
    let mut vec = Vec::new();
    let indx = records.0.get(feat).unwrap();
    let i = records.0.len();
    let j = records.1.len();

    for y in 0..j {
        vec.push(records.2[(y * i + *indx as usize) as usize]);
    }

    vec
}

fn get_user_avg(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>)) -> Vec<f32> {
    let mut vec = Vec::new();
    let i = records.0.len();
    let j = records.1.len();

    for y in 0..j {
        let mut rated = 0;
        let mut avg = 0.0;
        for x in 0..i {
            let rating = records.2[(y * i + x) as usize];
            if rating != 0.0 {
                avg += rating;
                rated += 1;
            }
        }
        avg /= rated as f32;
        vec.push(avg);
    }

    vec
}

fn normalize(vect: &Vec<f32>) -> (Vec<f32>, f32, f32) {
    let mut normd_vec = Vec::with_capacity(vect.len());

    let mut max = vect[0];
    let mut min = vect[0];

    for rating in vect.iter() {
        if *rating > max {
            max = *rating;
        }
        if *rating != 0.0 && *rating < min || min == 0.0 {
            min = *rating;
        }
    }

    for rating in vect.iter() {
        if *rating > 0.0 {
            let norm = (2.0 * (rating - min) - (max - min)) / (max - min);
            normd_vec.push(norm);
        }
    }

    (normd_vec, max, min)
}

fn unnormalize(norm_val: f32, max: f32, min: f32) -> f32 {
    0.5 * (norm_val + 1.0) * (max - min) + min
}

fn load_database(path: &str) -> (HashMap<String, u32>, HashMap<String, u32>, Vec<f32>) {
    let mut rdr = csv::Reader::from_file(path).unwrap().has_headers(true);
    let mut headers: HashMap<String, u32> = HashMap::new();
    let mut names: HashMap<String, u32> = HashMap::new();
    let mut records: Vec<f32> = Vec::new();

    let mut i = 0;
    let mut j = 0;

    loop {
        match rdr.next_bytes() {
            csv::NextField::Data(data) => {
                let d_string = String::from_utf8(data.to_vec()).unwrap();
                match j {
                    0 => {
                        /***********HEADERS***********/
                        if i > 0 {
                            headers.insert(d_string, i - 1);
                        }
                    }
                    _ => {
                        /***********RECORDS***********/
                        match i {
                            0 => {
                                /***********NAMES***********/
                                names.insert(d_string, j - 1);
                            }
                            _ => {
                                /***********VALUES***********/
                                records.push(d_string.parse::<f32>().unwrap());
                            }
                        }
                    }
                }
                i += 1;
            }
            csv::NextField::EndOfRecord => {
                j += 1;
                i = 0;
            }
            csv::NextField::EndOfCsv => break,
            csv::NextField::Error(err) => panic!(err),
        }
    }

    (headers, names, records)
}

fn item_similarity(records: &(HashMap<String, u32>, HashMap<String, u32>, Vec<f32>),
                   feat_1: &str,
                   feat_2: &str)
                   -> f32 {
    let v_feat_1 = get_feature_vector(&records, feat_1);
    let v_feat_2 = get_feature_vector(&records, feat_2);
    let v_avg = get_user_avg(&records);

    adjusted_cosine(v_feat_1, v_feat_2, v_avg)
}

fn prediction(name: &str, feature: &str) -> f32 {
    // let records = load_database("./data/db1.csv");
    let records = load_database("./data/db1.1.csv");

    let user_vec = get_user_vector(&records, name);
    let normd_vec = normalize(&user_vec);
    let mut sim_vec = vec![0.0; normd_vec.0.len()];

    for (_feat, &_indx) in records.0.iter() {
        if _feat != feature {
            sim_vec[(_indx - 1) as usize] = item_similarity(&records, _feat, feature);
        }
    }

    let mut num = 0.0;
    let mut den = 0.0;
    let inv_i = sim_vec.len();
    for i in 0..inv_i {
        num += sim_vec[i] * normd_vec.0[i];
        den += sim_vec[i].abs();
    }

    let norm_pred = num / den;
    println!("Normalized prediction: {}", norm_pred);
    unnormalize(norm_pred, normd_vec.1, normd_vec.2)
}

fn main() {
    println!("Prediction: {}", prediction("David", "Kacey Musgraves"));
}
<|endoftext|>


<commit_before>extern crate csv;
extern crate rustc_serialize;

use std::fs::File;
use csv::Reader;

fn main() {
    println!("Hello, world!");
}
<commit_msg>Changes...<commit_after>extern crate csv;
extern crate rustc_serialize;

use std::fs::File;
use csv::Reader;

fn init_db_reader(path: &str) -> Reader<File> {
    let rdr = csv::Reader::from_file(path).unwrap();
    rdr
}

fn load_database() {
    let mut rdr = init_db_reader("./data/db1.csv").has_headers(true);
    let mut ratings: HashMap<String, HashMap<String, u32>> = HashMap::new();

    while !rdr.done() {

    }

}

fn similarity(user: &str, band: &str, k: u32, fun: fn(&Vec<f32>, &Vec<f32>) -> f32 {

}

fn main() {
    println!("Sim: {}", similarity("Dan", ));
    println!("Hello, world!");
}
<|endoftext|>