<commit_before>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let subdir = project_path.components().nth(0).unwrap();
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  transfer_to_tmp(newrev);

  let module_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let module_commit = module_commit_obj.as_commit().unwrap();
  let module_tree = module_commit.tree().unwrap();

  let master_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let master_commit = master_commit_obj.as_commit().unwrap();
  let master_tree = master_commit_obj.as_commit().unwrap().tree().unwrap();

  let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
  let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

  let parents = vec!(master_commit);
  let central_commit = make_commit(&tmp_repo, &new_tree, module_commit, &parents);
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(central_commit.unwrap()).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
  );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stdout));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stdout));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {
  println!("print some stuff to stdout");
  writeln!(&mut std::io::stderr(), "print some stuff to stderr");

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .arg(clap::Arg::with_name("change").long("change").takes_value(true))
      .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
      .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
      .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
      .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
      .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<commit_msg>nicer output<commit_after>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let subdir = project_path.components().nth(0).unwrap();
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  transfer_to_tmp(newrev);

  let module_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let module_commit = module_commit_obj.as_commit().unwrap();
  let module_tree = module_commit.tree().unwrap();

  let master_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let master_commit = master_commit_obj.as_commit().unwrap();
  let master_tree = master_commit_obj.as_commit().unwrap().tree().unwrap();

  let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
  let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

  let parents = vec!(master_commit);
  let central_commit = make_commit(&tmp_repo, &new_tree, module_commit, &parents);
  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(central_commit.unwrap()).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
  );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stderr));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .arg(clap::Arg::with_name("change").long("change").takes_value(true))
      .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
      .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
      .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
      .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
      .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate log;
extern crate clockpro_cache;
extern crate bytes;
extern crate clap;
extern crate coarsetime;
extern crate env_logger;
extern crate mio;
extern crate nix;
extern crate privdrop;
extern crate rand;
extern crate siphasher;
extern crate slab;
extern crate toml;

#[cfg(feature = "webservice")]
extern crate hyper;

#[macro_use]
extern crate prometheus;

mod cache;
mod client_query;
mod client;
mod config;
mod dns;
mod net_helpers;
mod resolver;
mod tcp_listener;
mod udp_listener;
mod varz;

#[cfg(feature = "webservice")]
mod webservice;

use cache::Cache;
use clap::{Arg, App};
use config::Config;
use net_helpers::*;
use privdrop::PrivDrop;
use resolver::*;
use std::net::{self, UdpSocket};
use std::sync::Arc;
use std::sync::mpsc;
use std::thread;
use tcp_listener::*;
use udp_listener::*;
use varz::*;

#[cfg(feature = "webservice")]
use webservice::*;

const DNS_MAX_SIZE: usize = 65535;
const DNS_MAX_TCP_SIZE: usize = 65535;
const DNS_MAX_UDP_SIZE: usize = 4096;
const DNS_QUERY_MAX_SIZE: usize = 283;
const DNS_QUERY_MIN_SIZE: usize = 17;
const DNS_UDP_NOEDNS0_MAX_SIZE: usize = 512;
const HEALTH_CHECK_MS: u64 = 10 * 1000;
const MAX_ACTIVE_QUERIES: usize = 100_000;
const MAX_CLIENTS_WAITING_FOR_QUERY: usize = 1_000;
const MAX_EVENTS_PER_BATCH: usize = 1024;
const MAX_TCP_CLIENTS: usize = 1_000;
const MAX_TCP_HASH_DISTANCE: usize = 10;
const MAX_TCP_IDLE_MS: u64 = 10 * 1000;
const MAX_WAITING_CLIENTS: usize = MAX_ACTIVE_QUERIES * 10;
const FAILURE_TTL: u32 = 30;
const TCP_BACKLOG: usize = 1024;
const UDP_BUFFER_SIZE: usize = 16 * 1024 * 1024;
const UPSTREAM_INITIAL_TIMEOUT_MS: u64 = 1 * 1000;
const UPSTREAM_MAX_TIMEOUT_MS: u64 = 3 * 1000;
const UPSTREAM_TIMEOUT_MS: u64 = 5 * 1000;

#[cfg(feature = "webservice")]
const WEBSERVICE_THREADS: usize = 1;

pub struct EdgeDNSContext {
    pub config: Config,
    pub listen_addr: String,
    pub udp_socket: UdpSocket,
    pub tcp_socket: net::TcpListener,
    pub cache: Cache,
    pub varz: Arc<Varz>,
}

struct EdgeDNS;

impl EdgeDNS {
    #[cfg(feature = "webservice")]
    fn webservice_start(edgedns_context: &EdgeDNSContext,
                        service_ready_tx: mpsc::SyncSender<u8>)
                        -> thread::JoinHandle<()> {
        WebService::spawn(edgedns_context, service_ready_tx)
            .expect("Unable to spawn the web service")
    }

    #[cfg(not(feature = "webservice"))]
    fn webservice_start(_edgedns_context: &EdgeDNSContext,
                        _service_ready_tx: mpsc::SyncSender<u8>) {
        debug!("This build was not compiled with support for webservices");
    }

    fn privileges_drop(config: &Config) {
        let mut pd = PrivDrop::default();
        if let Some(ref user) = config.user {
            pd = pd.user(user);
        }
        if let Some(ref group) = config.group {
            pd = pd.group(group);
        }
        if let Some(ref chroot_dir) = config.chroot_dir {
            pd = pd.chroot(chroot_dir);
        }
        pd.apply().unwrap();
    }

    fn new(config: Config) -> EdgeDNS {
        let ct = coarsetime::Updater::new(100)
            .start()
            .expect("Unable to spawn the internal timer");
        let varz = Arc::new(Varz::new());
        let cache = Cache::new(config.clone());
        let udp_socket = socket_udp_bound(&config.listen_addr)
            .expect("Unable to create a UDP client socket");
        let tcp_socket = socket_tcp_bound(&config.listen_addr)
            .expect("Unable to create a TCP client socket");
        let edgedns_context = EdgeDNSContext {
            config: config.clone(),
            listen_addr: config.listen_addr.to_owned(),
            udp_socket: udp_socket,
            tcp_socket: tcp_socket,
            cache: cache,
            varz: varz,
        };
        let resolver_tx = Resolver::spawn(&edgedns_context).expect("Unable to spawn the resolver");
        let (service_ready_tx, service_ready_rx) = mpsc::sync_channel::<u8>(1);
        let mut tasks: Vec<thread::JoinHandle<()>> = Vec::new();
        for _ in 0..config.udp_listener_threads {
            let udp_listener = UdpListener::spawn(&edgedns_context,
                                                  resolver_tx.clone(),
                                                  service_ready_tx.clone())
                .expect("Unable to spawn a UDP listener");
            tasks.push(udp_listener);
            service_ready_rx.recv().unwrap();
        }
        for _ in 0..config.tcp_listener_threads {
            let tcp_listener = TcpListener::spawn(&edgedns_context,
                                                  resolver_tx.clone(),
                                                  service_ready_tx.clone())
                .expect("Unable to spawn a TCP listener");
            tasks.push(tcp_listener);
            service_ready_rx.recv().unwrap();
        }
        if config.webservice_enabled {
            let webservice = Self::webservice_start(&edgedns_context, service_ready_tx.clone());
            tasks.push(webservice);
            service_ready_rx.recv().unwrap();
        }
        Self::privileges_drop(&config);
        info!("EdgeDNS is ready to process requests");
        for task in tasks {
            let _ = task.join();
        }
        ct.stop().unwrap();
        EdgeDNS
    }
}

fn main() {
    env_logger::init().expect("Failed to init logger");

    let matches = App::new("EdgeDNS")
        .version("0.2.1")
        .author("Frank Denis")
        .about("A caching DNS reverse proxy")
        .arg(Arg::with_name("config_file")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Path to the edgedns.toml config file")
            .takes_value(true)
            .required(true))
        .get_matches();

    let config_file = match matches.value_of("config_file") {
        None => {
            error!("A path to the configuration file is required");
            return;
        }
        Some(config_file) => config_file,
    };
    let config = match Config::from_path(config_file) {
        Err(err) => {
            error!("The configuration couldn't be loaded -- [{}]: [{}]",
                   config_file,
                   err);
            return;
        }
        Ok(config) => config,
    };
    EdgeDNS::new(config);
}
<commit_msg>At least, do not hardcode the clock resolution<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate log;
extern crate clockpro_cache;
extern crate bytes;
extern crate clap;
extern crate coarsetime;
extern crate env_logger;
extern crate mio;
extern crate nix;
extern crate privdrop;
extern crate rand;
extern crate siphasher;
extern crate slab;
extern crate toml;

#[cfg(feature = "webservice")]
extern crate hyper;

#[macro_use]
extern crate prometheus;

mod cache;
mod client_query;
mod client;
mod config;
mod dns;
mod net_helpers;
mod resolver;
mod tcp_listener;
mod udp_listener;
mod varz;

#[cfg(feature = "webservice")]
mod webservice;

use cache::Cache;
use clap::{Arg, App};
use config::Config;
use net_helpers::*;
use privdrop::PrivDrop;
use resolver::*;
use std::net::{self, UdpSocket};
use std::sync::Arc;
use std::sync::mpsc;
use std::thread;
use tcp_listener::*;
use udp_listener::*;
use varz::*;

#[cfg(feature = "webservice")]
use webservice::*;

const CLOCK_RESOLUTION: u64 = 100;
const DNS_MAX_SIZE: usize = 65535;
const DNS_MAX_TCP_SIZE: usize = 65535;
const DNS_MAX_UDP_SIZE: usize = 4096;
const DNS_QUERY_MAX_SIZE: usize = 283;
const DNS_QUERY_MIN_SIZE: usize = 17;
const DNS_UDP_NOEDNS0_MAX_SIZE: usize = 512;
const HEALTH_CHECK_MS: u64 = 10 * 1000;
const MAX_ACTIVE_QUERIES: usize = 100_000;
const MAX_CLIENTS_WAITING_FOR_QUERY: usize = 1_000;
const MAX_EVENTS_PER_BATCH: usize = 1024;
const MAX_TCP_CLIENTS: usize = 1_000;
const MAX_TCP_HASH_DISTANCE: usize = 10;
const MAX_TCP_IDLE_MS: u64 = 10 * 1000;
const MAX_WAITING_CLIENTS: usize = MAX_ACTIVE_QUERIES * 10;
const FAILURE_TTL: u32 = 30;
const TCP_BACKLOG: usize = 1024;
const UDP_BUFFER_SIZE: usize = 16 * 1024 * 1024;
const UPSTREAM_INITIAL_TIMEOUT_MS: u64 = 1 * 1000;
const UPSTREAM_MAX_TIMEOUT_MS: u64 = 3 * 1000;
const UPSTREAM_TIMEOUT_MS: u64 = 5 * 1000;

#[cfg(feature = "webservice")]
const WEBSERVICE_THREADS: usize = 1;

pub struct EdgeDNSContext {
    pub config: Config,
    pub listen_addr: String,
    pub udp_socket: UdpSocket,
    pub tcp_socket: net::TcpListener,
    pub cache: Cache,
    pub varz: Arc<Varz>,
}

struct EdgeDNS;

impl EdgeDNS {
    #[cfg(feature = "webservice")]
    fn webservice_start(edgedns_context: &EdgeDNSContext,
                        service_ready_tx: mpsc::SyncSender<u8>)
                        -> thread::JoinHandle<()> {
        WebService::spawn(edgedns_context, service_ready_tx)
            .expect("Unable to spawn the web service")
    }

    #[cfg(not(feature = "webservice"))]
    fn webservice_start(_edgedns_context: &EdgeDNSContext,
                        _service_ready_tx: mpsc::SyncSender<u8>) {
        debug!("This build was not compiled with support for webservices");
    }

    fn privileges_drop(config: &Config) {
        let mut pd = PrivDrop::default();
        if let Some(ref user) = config.user {
            pd = pd.user(user);
        }
        if let Some(ref group) = config.group {
            pd = pd.group(group);
        }
        if let Some(ref chroot_dir) = config.chroot_dir {
            pd = pd.chroot(chroot_dir);
        }
        pd.apply().unwrap();
    }

    fn new(config: Config) -> EdgeDNS {
        let ct = coarsetime::Updater::new(CLOCK_RESOLUTION)
            .start()
            .expect("Unable to spawn the internal timer");
        let varz = Arc::new(Varz::new());
        let cache = Cache::new(config.clone());
        let udp_socket = socket_udp_bound(&config.listen_addr)
            .expect("Unable to create a UDP client socket");
        let tcp_socket = socket_tcp_bound(&config.listen_addr)
            .expect("Unable to create a TCP client socket");
        let edgedns_context = EdgeDNSContext {
            config: config.clone(),
            listen_addr: config.listen_addr.to_owned(),
            udp_socket: udp_socket,
            tcp_socket: tcp_socket,
            cache: cache,
            varz: varz,
        };
        let resolver_tx = Resolver::spawn(&edgedns_context).expect("Unable to spawn the resolver");
        let (service_ready_tx, service_ready_rx) = mpsc::sync_channel::<u8>(1);
        let mut tasks: Vec<thread::JoinHandle<()>> = Vec::new();
        for _ in 0..config.udp_listener_threads {
            let udp_listener = UdpListener::spawn(&edgedns_context,
                                                  resolver_tx.clone(),
                                                  service_ready_tx.clone())
                .expect("Unable to spawn a UDP listener");
            tasks.push(udp_listener);
            service_ready_rx.recv().unwrap();
        }
        for _ in 0..config.tcp_listener_threads {
            let tcp_listener = TcpListener::spawn(&edgedns_context,
                                                  resolver_tx.clone(),
                                                  service_ready_tx.clone())
                .expect("Unable to spawn a TCP listener");
            tasks.push(tcp_listener);
            service_ready_rx.recv().unwrap();
        }
        if config.webservice_enabled {
            let webservice = Self::webservice_start(&edgedns_context, service_ready_tx.clone());
            tasks.push(webservice);
            service_ready_rx.recv().unwrap();
        }
        Self::privileges_drop(&config);
        info!("EdgeDNS is ready to process requests");
        for task in tasks {
            let _ = task.join();
        }
        ct.stop().unwrap();
        EdgeDNS
    }
}

fn main() {
    env_logger::init().expect("Failed to init logger");

    let matches = App::new("EdgeDNS")
        .version("0.2.1")
        .author("Frank Denis")
        .about("A caching DNS reverse proxy")
        .arg(Arg::with_name("config_file")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Path to the edgedns.toml config file")
            .takes_value(true)
            .required(true))
        .get_matches();

    let config_file = match matches.value_of("config_file") {
        None => {
            error!("A path to the configuration file is required");
            return;
        }
        Some(config_file) => config_file,
    };
    let config = match Config::from_path(config_file) {
        Err(err) => {
            error!("The configuration couldn't be loaded -- [{}]: [{}]",
                   config_file,
                   err);
            return;
        }
        Ok(config) => config,
    };
    EdgeDNS::new(config);
}
<|endoftext|>


<commit_before>//! Serde Serialization Framework
//!
//! Serde is a powerful framework that enables serialization libraries to generically serialize
//! Rust data structures without the overhead of runtime type information. In many situations, the
//! handshake protocol between serializers and serializees can be completely optimized away,
//! leaving serde to perform roughly the same speed as a hand written serializer for a specific
//! type.
//!
//! For a detailed tutorial on the different ways to use serde please check out the
//! [github repository](https://github.com/serde-rs/serde)

#![doc(html_root_url="https://docs.serde.rs")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "unstable", feature(reflect_marker, unicode, nonzero, plugin, step_trait, zero_one))]
#![cfg_attr(feature = "alloc", feature(alloc))]
#![cfg_attr(feature = "collections", feature(collections, enumset))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", allow(linkedlist))]

#![cfg_attr(any(not(feature = "std"), feature = "unstable"), allow(unused_variables, unused_imports, unused_features, dead_code))]

#![deny(missing_docs)]

#[cfg(all(feature = "unstable", feature = "collections"))]
extern crate collections;

#[cfg(all(feature = "unstable", feature = "alloc"))]
extern crate alloc;

#[cfg(feature = "std")]
mod core {
    pub use std::{ops, hash, fmt, cmp, marker, mem, i8, i16, i32, i64, u8, u16, u32, u64, isize,
            usize, f32, f64, char, str, num, slice, iter};
    #[cfg(feature = "unstable")]
    extern crate core;
    #[cfg(feature = "unstable")]
    pub use self::core::nonzero;
}

pub use ser::{Serialize, Serializer};
pub use de::{Deserialize, Deserializer, Error};

#[cfg(not(feature = "std"))]
macro_rules! format {
    ($s:expr, $($rest:tt)*) => ($s)
}

#[macro_use]
mod macros;

pub mod bytes;
pub mod de;
#[cfg(feature = "std")]
pub mod iter;
pub mod ser;
#[cfg(not(feature = "std"))]
pub mod error;
mod utils;
<commit_msg>Stop exporting serde::de::Error as serde::Error<commit_after>//! Serde Serialization Framework
//!
//! Serde is a powerful framework that enables serialization libraries to generically serialize
//! Rust data structures without the overhead of runtime type information. In many situations, the
//! handshake protocol between serializers and serializees can be completely optimized away,
//! leaving serde to perform roughly the same speed as a hand written serializer for a specific
//! type.
//!
//! For a detailed tutorial on the different ways to use serde please check out the
//! [github repository](https://github.com/serde-rs/serde)

#![doc(html_root_url="https://docs.serde.rs")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "unstable", feature(reflect_marker, unicode, nonzero, plugin, step_trait, zero_one))]
#![cfg_attr(feature = "alloc", feature(alloc))]
#![cfg_attr(feature = "collections", feature(collections, enumset))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", allow(linkedlist))]

#![cfg_attr(any(not(feature = "std"), feature = "unstable"), allow(unused_variables, unused_imports, unused_features, dead_code))]

#![deny(missing_docs)]

#[cfg(all(feature = "unstable", feature = "collections"))]
extern crate collections;

#[cfg(all(feature = "unstable", feature = "alloc"))]
extern crate alloc;

#[cfg(feature = "std")]
mod core {
    pub use std::{ops, hash, fmt, cmp, marker, mem, i8, i16, i32, i64, u8, u16, u32, u64, isize,
            usize, f32, f64, char, str, num, slice, iter};
    #[cfg(feature = "unstable")]
    extern crate core;
    #[cfg(feature = "unstable")]
    pub use self::core::nonzero;
}

pub use ser::{Serialize, Serializer};
pub use de::{Deserialize, Deserializer};

#[cfg(not(feature = "std"))]
macro_rules! format {
    ($s:expr, $($rest:tt)*) => ($s)
}

#[macro_use]
mod macros;

pub mod bytes;
pub mod de;
#[cfg(feature = "std")]
pub mod iter;
pub mod ser;
#[cfg(not(feature = "std"))]
pub mod error;
mod utils;
<|endoftext|>


<commit_before>use std::mem::MaybeUninit;
use std::slice;

use crate::well_known_types;

/// `Vec::spare_capacity_mut` is not stable until Rust 1.60.
pub(crate) fn vec_spare_capacity_mut<A>(vec: &mut Vec<A>) -> &mut [MaybeUninit<A>] {
    // SAFETY: copy-paste from rust stdlib.
    unsafe {
        slice::from_raw_parts_mut(
            vec.as_mut_ptr().add(vec.len()) as *mut MaybeUninit<A>,
            vec.capacity() - vec.len(),
        )
    }
}

/// `MaybeUninit::write_slice` is not stable.
pub(crate) fn maybe_uninit_write_slice<T>(this: &mut [MaybeUninit<T>], src: &[T])
where
    T: Copy,
{
    // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
    let uninit_src: &[MaybeUninit<T>] = unsafe { std::mem::transmute(src) };

    this.copy_from_slice(uninit_src);
}

// bool <-> BoolValue

impl From<well_known_types::BoolValue> for bool {
    fn from(inner: well_known_types::BoolValue) -> Self {
        inner.value
    }
}

impl From<bool> for well_known_types::BoolValue {
    fn from(inner: bool) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// Vec<u8> <-> BytesValue

impl From<well_known_types::BytesValue> for Vec<u8> {
    fn from(inner: well_known_types::BytesValue) -> Self {
        inner.value
    }
}

impl From<Vec<u8>> for well_known_types::BytesValue {
    fn from(inner: Vec<u8>) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// f64 <-> DoubleValue

impl From<well_known_types::DoubleValue> for f64 {
    fn from(inner: well_known_types::DoubleValue) -> Self {
        inner.value
    }
}

impl From<f64> for well_known_types::DoubleValue {
    fn from(inner: f64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// f32 <-> FloatValue

impl From<well_known_types::FloatValue> for f32 {
    fn from(inner: well_known_types::FloatValue) -> Self {
        inner.value
    }
}

impl From<f32> for well_known_types::FloatValue {
    fn from(inner: f32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// i32 <-> Int32Value

impl From<well_known_types::Int32Value> for i32 {
    fn from(inner: well_known_types::Int32Value) -> Self {
        inner.value
    }
}

impl From<i32> for well_known_types::Int32Value {
    fn from(inner: i32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// i64 <-> Int64Value

impl From<well_known_types::Int64Value> for i64 {
    fn from(inner: well_known_types::Int64Value) -> Self {
        inner.value
    }
}

impl From<i64> for well_known_types::Int64Value {
    fn from(inner: i64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// u32 <-> UInt32Value

impl From<well_known_types::UInt32Value> for u32 {
    fn from(inner: well_known_types::UInt32Value) -> Self {
        inner.value
    }
}

impl From<u32> for well_known_types::UInt32Value {
    fn from(inner: u32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// u64 <-> UInt64Value

impl From<well_known_types::UInt64Value> for u64 {
    fn from(inner: well_known_types::UInt64Value) -> Self {
        inner.value
    }
}

impl From<u64> for well_known_types::UInt64Value {
    fn from(inner: u64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// () <-> Empty

impl From<well_known_types::Empty> for () {
    fn from(_inner: well_known_types::Empty) -> Self {}
}

impl From<()> for well_known_types::Empty {
    fn from(_inner: ()) -> Self {
        Self::new()
    }
}

// TODO Think about `std::time::Duration` and `std::time::SystemTime` conversions.
<commit_msg>maybe_uninit_write_slice tweaks<commit_after>use std::mem;
use std::mem::MaybeUninit;
use std::slice;

use crate::well_known_types;

/// `Vec::spare_capacity_mut` is not stable until Rust 1.60.
pub(crate) fn vec_spare_capacity_mut<A>(vec: &mut Vec<A>) -> &mut [MaybeUninit<A>] {
    // SAFETY: copy-paste from rust stdlib.
    unsafe {
        slice::from_raw_parts_mut(
            vec.as_mut_ptr().add(vec.len()) as *mut MaybeUninit<A>,
            vec.capacity() - vec.len(),
        )
    }
}

/// `MaybeUninit::write_slice` is not stable.
pub(crate) fn maybe_uninit_write_slice<'a, T>(
    this: &'a mut [MaybeUninit<T>],
    src: &[T],
) -> &'a mut [T]
where
    T: Copy,
{
    // SAFETY: copy-paste from rust stdlib.

    let uninit_src: &[MaybeUninit<T>] = unsafe { mem::transmute(src) };

    this.copy_from_slice(uninit_src);

    unsafe { &mut *(this as *mut [MaybeUninit<T>] as *mut [T]) }
}

// bool <-> BoolValue

impl From<well_known_types::BoolValue> for bool {
    fn from(inner: well_known_types::BoolValue) -> Self {
        inner.value
    }
}

impl From<bool> for well_known_types::BoolValue {
    fn from(inner: bool) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// Vec<u8> <-> BytesValue

impl From<well_known_types::BytesValue> for Vec<u8> {
    fn from(inner: well_known_types::BytesValue) -> Self {
        inner.value
    }
}

impl From<Vec<u8>> for well_known_types::BytesValue {
    fn from(inner: Vec<u8>) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// f64 <-> DoubleValue

impl From<well_known_types::DoubleValue> for f64 {
    fn from(inner: well_known_types::DoubleValue) -> Self {
        inner.value
    }
}

impl From<f64> for well_known_types::DoubleValue {
    fn from(inner: f64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// f32 <-> FloatValue

impl From<well_known_types::FloatValue> for f32 {
    fn from(inner: well_known_types::FloatValue) -> Self {
        inner.value
    }
}

impl From<f32> for well_known_types::FloatValue {
    fn from(inner: f32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// i32 <-> Int32Value

impl From<well_known_types::Int32Value> for i32 {
    fn from(inner: well_known_types::Int32Value) -> Self {
        inner.value
    }
}

impl From<i32> for well_known_types::Int32Value {
    fn from(inner: i32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// i64 <-> Int64Value

impl From<well_known_types::Int64Value> for i64 {
    fn from(inner: well_known_types::Int64Value) -> Self {
        inner.value
    }
}

impl From<i64> for well_known_types::Int64Value {
    fn from(inner: i64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// u32 <-> UInt32Value

impl From<well_known_types::UInt32Value> for u32 {
    fn from(inner: well_known_types::UInt32Value) -> Self {
        inner.value
    }
}

impl From<u32> for well_known_types::UInt32Value {
    fn from(inner: u32) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// u64 <-> UInt64Value

impl From<well_known_types::UInt64Value> for u64 {
    fn from(inner: well_known_types::UInt64Value) -> Self {
        inner.value
    }
}

impl From<u64> for well_known_types::UInt64Value {
    fn from(inner: u64) -> Self {
        let mut value = Self::new();
        value.value = inner;
        value
    }
}

// () <-> Empty

impl From<well_known_types::Empty> for () {
    fn from(_inner: well_known_types::Empty) -> Self {}
}

impl From<()> for well_known_types::Empty {
    fn from(_inner: ()) -> Self {
        Self::new()
    }
}

// TODO Think about `std::time::Duration` and `std::time::SystemTime` conversions.
<|endoftext|>


<commit_before>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Mocked types for client-side and server-side APIs.
use std::cell::{Cell, RefCell};
use std::io::{self, Read, Write};
use std::sync::{Once, ONCE_INIT};
use std::{fmt, ptr, thread};

use log::{Metadata, Record};

use rand::{self, Rng, ThreadRng};

/// A mock implementation of `client::HttpRequest` which can spawn an `HttpBuffer`.
///
/// `client::HttpRequest` impl requires the `client` feature.
#[derive(Default, Debug)]
pub struct ClientRequest {
    boundary: Option<String>,
    content_len: Option<u64>,
}

#[cfg(feature = "client")]
impl ::client::HttpRequest for ClientRequest {
    type Stream = HttpBuffer;
    type Error = io::Error;

    fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool {
        self.boundary = Some(boundary.into());
        self.content_len = content_len;
        true
    }

    /// ## Panics
    /// If `apply_headers()` was not called.
    fn open_stream(self) -> Result<HttpBuffer, io::Error> {
        debug!("ClientRequest::open_stream called! {:?}", self);
        let boundary = self.boundary.expect("ClientRequest::set_headers() was not called!");

        Ok(HttpBuffer::new_empty(boundary, self.content_len))
    }
}

/// A writable buffer which stores the boundary and content-length, if provided.
///
/// Implements `client::HttpStream` if the `client` feature is enabled.
pub struct HttpBuffer {
    /// The buffer containing the raw bytes.
    pub buf: Vec<u8>,
    /// The multipart boundary.
    pub boundary: String,
    /// The value of the content-length header, if set.
    pub content_len: Option<u64>,
    rng: ThreadRng,
}

impl HttpBuffer {
    /// Create an empty buffer with the given boundary and optional content-length.
    pub fn new_empty(boundary: String, content_len: Option<u64>) -> HttpBuffer {
        Self::with_buf(Vec::new(), boundary, content_len)
    }

    /// Wrap the given buffer with the given boundary and optional content-length.
    pub fn with_buf(buf: Vec<u8>, boundary: String, content_len: Option<u64>) -> Self {
        HttpBuffer {
            buf: buf,
            boundary: boundary,
            content_len: content_len,
            rng: rand::thread_rng()
        }
    }

    /// Get a `ServerRequest` wrapping the data in this buffer.
    pub fn for_server(&self) -> ServerRequest {
        ServerRequest {
            data: &self.buf,
            boundary: &self.boundary,
            content_len: self.content_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Write for HttpBuffer {
    /// To simulate a network connection, this will copy a random number of bytes
    /// from `buf` to the buffer.
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if buf.is_empty() {
            debug!("HttpBuffer::write() was passed a zero-sized buffer.");
            return Ok(0);
        }

        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, buf.len() + 1);

        self.buf.write(&buf[..len])
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

#[cfg(feature = "client")]
impl ::client::HttpStream for HttpBuffer {
    type Request = ClientRequest;
    type Response = HttpBuffer;
    type Error = io::Error;

    /// Returns `Ok(self)`.
    fn finish(self) -> Result<Self, io::Error> { Ok(self) }
}

impl fmt::Debug for HttpBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("multipart::mock::HttpBuffer")
            .field("buf", &self.buf)
            .field("boundary", &self.boundary)
            .field("content_len", &self.content_len)
            .finish()
    }
}

/// A mock implementation of `server::HttpRequest` that can be read.
///
/// Implements `server::HttpRequest` if the `server` feature is enabled.
pub struct ServerRequest<'a> {
    /// Slice of the source `HttpBuffer::buf`
    pub data: &'a [u8],
    /// The multipart boundary.
    pub boundary: &'a str,
    /// The value of the content-length header, if set.
    pub content_len: Option<u64>,
    rng: ThreadRng,
}

impl<'a> ServerRequest<'a> {
    /// Create a new `ServerRequest` with the given data and boundary.
    ///
    /// Assumes `content_len: None`
    pub fn new(data: &'a [u8], boundary: &'a str) -> Self {
        ServerRequest {
            data: data,
            boundary: boundary,
            content_len: None,
            rng: rand::thread_rng(),
        }
    }
}

impl<'a> Read for ServerRequest<'a> {
    /// To simulate a network connection, this will copy a random number of bytes
    /// from the buffer to `out`.
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        if out.is_empty() {
            debug!("ServerRequest::read() was passed a zero-sized buffer.");
            return Ok(0);
        }

        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, out.len() + 1);
        self.data.read(&mut out[..len])
    }
}

#[cfg(feature = "server")]
impl<'a> ::server::HttpRequest for ServerRequest<'a> {
    type Body = Self;

    fn multipart_boundary(&self) -> Option<&str> { Some(self.boundary) }

    fn body(self) -> Self::Body {
        self
    }
}

/// A `Write` adapter that duplicates all data written to the inner writer as well as stdout.
pub struct StdoutTee<'s, W> {
    inner: W,
    stdout: io::StdoutLock<'s>,
}

impl<'s, W> StdoutTee<'s, W> {
    /// Constructor
    pub fn new(inner: W, stdout: &'s io::Stdout) -> Self {
        Self {
            inner, stdout: stdout.lock(),
        }
    }
}

impl<'s, W: Write> Write for StdoutTee<'s, W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)?;
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush();
        self.stdout.flush()
    }
}

/// Capture all logging on this thread during `PanicLogger`'s lifetime.
///
/// When `PanicLogger` is dropped: if the thread is panicking, all captured logs are printed
/// to stdout, otherwise the captured logs are deleted.
///
/// RFC: break this functionality out into its own crate? Could also support nested invocations
/// (where each instance of `PanicLogger` only prints the logs captured during its lifetime)
/// but that's currently unused here.
pub fn log_on_panic() -> PanicLogger {
    ::log::set_max_level(::log::LevelFilter::Trace);

    if ::log::logger() as *const ::log::Log != &LOGGER as *const ::log::Log {
        ::log::set_logger(&LOGGER).expect("failed to set logger for `log_on_panic()`");
    }

    LOG_ENABLED.with(|flag| flag.set(true));

    PanicLogger(())
}

/// Struct that logs if the thread panics before it is dropped.
#[must_use]
pub struct PanicLogger(());

impl PanicLogger {
    /// Clear the buffered logs
    pub fn clear(self) {
        drop(self);
    }
}

impl Drop for PanicLogger {
    fn drop(&mut self) {
        LOG_ENABLED.with(|flag| flag.set(false));
        LOG.with(|log| {
            let mut log = log.borrow_mut();
            if thread::panicking() {
                println!("captured logs before panic:\n{}", *log);
            }
            log.clear();
        });
    }
}

static INIT_PANIC_LOGGER: Once = ONCE_INIT;

thread_local! {
    static LOG: RefCell<String> = RefCell::new(String::new());
    static LOG_ENABLED: Cell<bool> = Cell::new(bool);
}

static LOGGER: ThreadLocalLogger = ThreadLocalLogger;

struct ThreadLocalLogger;

impl ::log::Log for ThreadLocalLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        LOG_ENABLED.with(Cell::get)
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            LOG.with(|log| {
                // format the message before pushing it to `ptr` since the fmt
                // impls of the record arguments could try to log as well
                let msg = format!("{}:{}: {}\n", record.level(), record.target(), record.args());
                log.borrow_mut().push_str(&msg);
            });
        }
    }

    fn flush(&self) {}
}
<commit_msg>fix typo<commit_after>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Mocked types for client-side and server-side APIs.
use std::cell::{Cell, RefCell};
use std::io::{self, Read, Write};
use std::sync::{Once, ONCE_INIT};
use std::{fmt, ptr, thread};

use log::{Metadata, Record};

use rand::{self, Rng, ThreadRng};

/// A mock implementation of `client::HttpRequest` which can spawn an `HttpBuffer`.
///
/// `client::HttpRequest` impl requires the `client` feature.
#[derive(Default, Debug)]
pub struct ClientRequest {
    boundary: Option<String>,
    content_len: Option<u64>,
}

#[cfg(feature = "client")]
impl ::client::HttpRequest for ClientRequest {
    type Stream = HttpBuffer;
    type Error = io::Error;

    fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool {
        self.boundary = Some(boundary.into());
        self.content_len = content_len;
        true
    }

    /// ## Panics
    /// If `apply_headers()` was not called.
    fn open_stream(self) -> Result<HttpBuffer, io::Error> {
        debug!("ClientRequest::open_stream called! {:?}", self);
        let boundary = self.boundary.expect("ClientRequest::set_headers() was not called!");

        Ok(HttpBuffer::new_empty(boundary, self.content_len))
    }
}

/// A writable buffer which stores the boundary and content-length, if provided.
///
/// Implements `client::HttpStream` if the `client` feature is enabled.
pub struct HttpBuffer {
    /// The buffer containing the raw bytes.
    pub buf: Vec<u8>,
    /// The multipart boundary.
    pub boundary: String,
    /// The value of the content-length header, if set.
    pub content_len: Option<u64>,
    rng: ThreadRng,
}

impl HttpBuffer {
    /// Create an empty buffer with the given boundary and optional content-length.
    pub fn new_empty(boundary: String, content_len: Option<u64>) -> HttpBuffer {
        Self::with_buf(Vec::new(), boundary, content_len)
    }

    /// Wrap the given buffer with the given boundary and optional content-length.
    pub fn with_buf(buf: Vec<u8>, boundary: String, content_len: Option<u64>) -> Self {
        HttpBuffer {
            buf: buf,
            boundary: boundary,
            content_len: content_len,
            rng: rand::thread_rng()
        }
    }

    /// Get a `ServerRequest` wrapping the data in this buffer.
    pub fn for_server(&self) -> ServerRequest {
        ServerRequest {
            data: &self.buf,
            boundary: &self.boundary,
            content_len: self.content_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Write for HttpBuffer {
    /// To simulate a network connection, this will copy a random number of bytes
    /// from `buf` to the buffer.
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if buf.is_empty() {
            debug!("HttpBuffer::write() was passed a zero-sized buffer.");
            return Ok(0);
        }

        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, buf.len() + 1);

        self.buf.write(&buf[..len])
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

#[cfg(feature = "client")]
impl ::client::HttpStream for HttpBuffer {
    type Request = ClientRequest;
    type Response = HttpBuffer;
    type Error = io::Error;

    /// Returns `Ok(self)`.
    fn finish(self) -> Result<Self, io::Error> { Ok(self) }
}

impl fmt::Debug for HttpBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("multipart::mock::HttpBuffer")
            .field("buf", &self.buf)
            .field("boundary", &self.boundary)
            .field("content_len", &self.content_len)
            .finish()
    }
}

/// A mock implementation of `server::HttpRequest` that can be read.
///
/// Implements `server::HttpRequest` if the `server` feature is enabled.
pub struct ServerRequest<'a> {
    /// Slice of the source `HttpBuffer::buf`
    pub data: &'a [u8],
    /// The multipart boundary.
    pub boundary: &'a str,
    /// The value of the content-length header, if set.
    pub content_len: Option<u64>,
    rng: ThreadRng,
}

impl<'a> ServerRequest<'a> {
    /// Create a new `ServerRequest` with the given data and boundary.
    ///
    /// Assumes `content_len: None`
    pub fn new(data: &'a [u8], boundary: &'a str) -> Self {
        ServerRequest {
            data: data,
            boundary: boundary,
            content_len: None,
            rng: rand::thread_rng(),
        }
    }
}

impl<'a> Read for ServerRequest<'a> {
    /// To simulate a network connection, this will copy a random number of bytes
    /// from the buffer to `out`.
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        if out.is_empty() {
            debug!("ServerRequest::read() was passed a zero-sized buffer.");
            return Ok(0);
        }

        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, out.len() + 1);
        self.data.read(&mut out[..len])
    }
}

#[cfg(feature = "server")]
impl<'a> ::server::HttpRequest for ServerRequest<'a> {
    type Body = Self;

    fn multipart_boundary(&self) -> Option<&str> { Some(self.boundary) }

    fn body(self) -> Self::Body {
        self
    }
}

/// A `Write` adapter that duplicates all data written to the inner writer as well as stdout.
pub struct StdoutTee<'s, W> {
    inner: W,
    stdout: io::StdoutLock<'s>,
}

impl<'s, W> StdoutTee<'s, W> {
    /// Constructor
    pub fn new(inner: W, stdout: &'s io::Stdout) -> Self {
        Self {
            inner, stdout: stdout.lock(),
        }
    }
}

impl<'s, W: Write> Write for StdoutTee<'s, W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)?;
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush();
        self.stdout.flush()
    }
}

/// Capture all logging on this thread during `PanicLogger`'s lifetime.
///
/// When `PanicLogger` is dropped: if the thread is panicking, all captured logs are printed
/// to stdout, otherwise the captured logs are deleted.
///
/// RFC: break this functionality out into its own crate? Could also support nested invocations
/// (where each instance of `PanicLogger` only prints the logs captured during its lifetime)
/// but that's currently unused here.
pub fn log_on_panic() -> PanicLogger {
    ::log::set_max_level(::log::LevelFilter::Trace);

    if ::log::logger() as *const ::log::Log != &LOGGER as *const ::log::Log {
        ::log::set_logger(&LOGGER).expect("failed to set logger for `log_on_panic()`");
    }

    LOG_ENABLED.with(|flag| flag.set(true));

    PanicLogger(())
}

/// Struct that logs if the thread panics before it is dropped.
#[must_use]
pub struct PanicLogger(());

impl PanicLogger {
    /// Clear the buffered logs
    pub fn clear(self) {
        drop(self);
    }
}

impl Drop for PanicLogger {
    fn drop(&mut self) {
        LOG_ENABLED.with(|flag| flag.set(false));
        LOG.with(|log| {
            let mut log = log.borrow_mut();
            if thread::panicking() {
                println!("captured logs before panic:\n{}", *log);
            }
            log.clear();
        });
    }
}

static INIT_PANIC_LOGGER: Once = ONCE_INIT;

thread_local! {
    static LOG: RefCell<String> = RefCell::new(String::new());
    static LOG_ENABLED: Cell<bool> = Cell::new(false);
}

static LOGGER: ThreadLocalLogger = ThreadLocalLogger;

struct ThreadLocalLogger;

impl ::log::Log for ThreadLocalLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        LOG_ENABLED.with(Cell::get)
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            LOG.with(|log| {
                // format the message before pushing it to `ptr` since the fmt
                // impls of the record arguments could try to log as well
                let msg = format!("{}:{}: {}\n", record.level(), record.target(), record.args());
                log.borrow_mut().push_str(&msg);
            });
        }
    }

    fn flush(&self) {}
}
<|endoftext|>


<commit_before>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        let directory_start = footer.central_directory_offset as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive { reader: reader, files: files, names_map: names_map })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        let reader = match data.compression_method
        {
            CompressionMethod::Stored =>
            {
                ZipFileReader::Stored(Crc32Reader::new(
                    limit_reader,
                    data.crc32))
            },
            CompressionMethod::Deflated =>
            {
                let deflate_reader = limit_reader.deflate_decode();
                ZipFileReader::Deflated(Crc32Reader::new(
                    deflate_reader,
                    data.crc32))
            },
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 =>
            {
                let bzip2_reader = BzDecoder::new(limit_reader);
                ZipFileReader::Bzip2(Crc32Reader::new(
                    bzip2_reader,
                    data.crc32))
            },
            _ => return unsupported_zip_error("Compression method not supported"),
        };
        Ok(ZipFile { reader: reader, data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        match self.reader {
           ZipFileReader::Stored(ref mut r) => r as &mut Read,
           ZipFileReader::Deflated(ref mut r) => r as &mut Read,
           #[cfg(feature = "bzip2")]
           ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
        }
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}
<commit_msg>Add data_start property to ZipFile<commit_after>//! Structs for reading a ZIP archive

use crc32::Crc32Reader;
use compression::CompressionMethod;
use spec;
use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use flate2;
use flate2::FlateReadExt;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};

#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;

mod ffi {
    pub const S_IFDIR: u32 = 0o0040000;
    pub const S_IFREG: u32 = 0o0100000;
}

/// Wrapper for reading the contents of a ZIP file.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::prelude::*;
///
///     // For demonstration purposes we read from an empty buffer.
///     // Normally a File object would be used.
///     let buf: &[u8] = &[0u8; 128];
///     let mut reader = std::io::Cursor::new(buf);
///
///     let mut zip = try!(zip::ZipArchive::new(reader));
///
///     for i in 0..zip.len()
///     {
///         let mut file = zip.by_index(i).unwrap();
///         println!("Filename: {}", file.name());
///         let first_byte = try!(file.bytes().next().unwrap());
///         println!("{}", first_byte);
///     }
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
#[derive(Debug)]
pub struct ZipArchive<R: Read + io::Seek>
{
    reader: R,
    files: Vec<ZipFileData>,
    names_map: HashMap<String, usize>,
}

enum ZipFileReader<'a> {
    Stored(Crc32Reader<io::Take<&'a mut Read>>),
    Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
    #[cfg(feature = "bzip2")]
    Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
}

/// A struct for reading a zip file
pub struct ZipFile<'a> {
    data: &'a ZipFileData,
    reader: ZipFileReader<'a>,
}

fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
{
    Err(ZipError::UnsupportedArchive(detail))
}

impl<R: Read+io::Seek> ZipArchive<R>
{
    /// Opens a Zip archive and parses the central directory
    pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
        let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));

        if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }

        let directory_start = footer.central_directory_offset as u64;
        let number_of_files = footer.number_of_files_on_this_disk as usize;

        let mut files = Vec::with_capacity(number_of_files);
        let mut names_map = HashMap::new();

        try!(reader.seek(io::SeekFrom::Start(directory_start)));
        for _ in 0 .. number_of_files
        {
            let file = try!(central_header_to_zip_file(&mut reader));
            names_map.insert(file.file_name.clone(), files.len());
            files.push(file);
        }

        Ok(ZipArchive { reader: reader, files: files, names_map: names_map })
    }

    /// Number of files contained in this zip.
    ///
    /// ```
    /// fn iter() {
    ///     let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
    ///
    ///     for i in 0..zip.len() {
    ///         let mut file = zip.by_index(i).unwrap();
    ///         // Do something with file i
    ///     }
    /// }
    /// ```
    pub fn len(&self) -> usize
    {
        self.files.len()
    }

    /// Search for a file entry by name
    pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
    {
        let index = match self.names_map.get(name) {
            Some(index) => *index,
            None => { return Err(ZipError::FileNotFound); },
        };
        self.by_index(index)
    }

    /// Get a contained file by index
    pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
    {
        if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
        let ref data = self.files[file_number];
        let pos = data.data_start;

        if data.encrypted
        {
            return unsupported_zip_error("Encrypted files are not supported")
        }

        try!(self.reader.seek(io::SeekFrom::Start(pos)));
        let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);

        let reader = match data.compression_method
        {
            CompressionMethod::Stored =>
            {
                ZipFileReader::Stored(Crc32Reader::new(
                    limit_reader,
                    data.crc32))
            },
            CompressionMethod::Deflated =>
            {
                let deflate_reader = limit_reader.deflate_decode();
                ZipFileReader::Deflated(Crc32Reader::new(
                    deflate_reader,
                    data.crc32))
            },
            #[cfg(feature = "bzip2")]
            CompressionMethod::Bzip2 =>
            {
                let bzip2_reader = BzDecoder::new(limit_reader);
                ZipFileReader::Bzip2(Crc32Reader::new(
                    bzip2_reader,
                    data.crc32))
            },
            _ => return unsupported_zip_error("Compression method not supported"),
        };
        Ok(ZipFile { reader: reader, data: data })
    }

    /// Unwrap and return the inner reader object
    ///
    /// The position of the reader is undefined.
    pub fn into_inner(self) -> R
    {
        self.reader
    }
}

fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
{
    // Parse central header
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
    }

    let version_made_by = try!(reader.read_u16::<LittleEndian>());
    let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
    let flags = try!(reader.read_u16::<LittleEndian>());
    let encrypted = flags & 1 == 1;
    let is_utf8 = flags & (1 << 11) != 0;
    let compression_method = try!(reader.read_u16::<LittleEndian>());
    let last_mod_time = try!(reader.read_u16::<LittleEndian>());
    let last_mod_date = try!(reader.read_u16::<LittleEndian>());
    let crc32 = try!(reader.read_u32::<LittleEndian>());
    let compressed_size = try!(reader.read_u32::<LittleEndian>());
    let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
    let _disk_number = try!(reader.read_u16::<LittleEndian>());
    let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
    let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
    let offset = try!(reader.read_u32::<LittleEndian>()) as u64;
    let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
    let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
    let file_comment_raw  = try!(ReadPodExt::read_exact(reader, file_comment_length));

    let file_name = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
        false => file_name_raw.clone().from_cp437(),
    };
    let file_comment = match is_utf8
    {
        true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
        false => file_comment_raw.from_cp437(),
    };

    // Remember end of central header
    let return_position = try!(reader.seek(io::SeekFrom::Current(0)));

    // Parse local header
    try!(reader.seek(io::SeekFrom::Start(offset)));
    let signature = try!(reader.read_u32::<LittleEndian>());
    if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(ZipError::InvalidArchive("Invalid local file header"))
    }

    try!(reader.seek(io::SeekFrom::Current(22)));
    let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let mut result = ZipFileData
    {
        system: System::from_u8((version_made_by >> 8) as u8),
        version_made_by: version_made_by as u8,
        encrypted: encrypted,
        compression_method: CompressionMethod::from_u16(compression_method),
        last_modified_time: try!(::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date))),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_name_raw: file_name_raw,
        file_comment: file_comment,
        header_start: offset,
        data_start: data_start,
        external_attributes: external_file_attributes,
    };

    match parse_extra_field(&mut result, &*extra_field) {
        Ok(..) | Err(ZipError::Io(..)) => {},
        Err(e) => try!(Err(e)),
    }

    // Go back after the central header
    try!(reader.seek(io::SeekFrom::Start(return_position)));

    Ok(result)
}

fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
    let mut reader = io::Cursor::new(data);

    while (reader.position() as usize) < data.len()
    {
        let kind = try!(reader.read_u16::<LittleEndian>());
        let len = try!(reader.read_u16::<LittleEndian>());
        match kind
        {
            _ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
        };
    }
    Ok(())
}

/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
    fn get_reader(&mut self) -> &mut Read {
        match self.reader {
           ZipFileReader::Stored(ref mut r) => r as &mut Read,
           ZipFileReader::Deflated(ref mut r) => r as &mut Read,
           #[cfg(feature = "bzip2")]
           ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
        }
    }
    /// Get the version of the file
    pub fn version_made_by(&self) -> (u8, u8) {
        (self.data.version_made_by / 10, self.data.version_made_by % 10)
    }
    /// Get the name of the file
    pub fn name(&self) -> &str {
        &*self.data.file_name
    }
    /// Get the name of the file, in the raw (internal) byte representation.
    pub fn name_raw(&self) -> &[u8] {
        &*self.data.file_name_raw
    }
    /// Get the comment of the file
    pub fn comment(&self) -> &str {
        &*self.data.file_comment
    }
    /// Get the compression method used to store the file
    pub fn compression(&self) -> CompressionMethod {
        self.data.compression_method
    }
    /// Get the size of the file in the archive
    pub fn compressed_size(&self) -> u64 {
        self.data.compressed_size
    }
    /// Get the size of the file when uncompressed
    pub fn size(&self) -> u64 {
        self.data.uncompressed_size
    }
    /// Get the time the file was last modified
    pub fn last_modified(&self) -> ::time::Tm {
        self.data.last_modified_time
    }
    /// Get unix mode for the file
    pub fn unix_mode(&self) -> Option<u32> {
        match self.data.system {
            System::Unix => {
                Some(self.data.external_attributes >> 16)
            },
            System::Dos => {
                // Interpret MSDOS directory bit
                let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
                    ffi::S_IFDIR | 0o0775
                } else {
                    ffi::S_IFREG | 0o0664
                };
                if 0x01 == (self.data.external_attributes & 0x01) {
                    // Read-only bit; strip write permissions
                    mode &= 0o0555;
                }
                Some(mode)
            },
            _ => None,
        }
    }
    /// Get the CRC32 hash of the original file
    pub fn crc32(&self) -> u32 {
        self.data.crc32
    }

    /// Get the starting offset of the data of the compressed file
    pub fn data_start(&self) -> u64 {
        self.data.data_start
    }
}

impl<'a> Read for ZipFile<'a> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_reader().read(buf)
     }
}
<|endoftext|>


<commit_before>use {Operation, Plan, Transform, c64};

impl Transform for [f64] {
    fn transform(&mut self, plan: &Plan) {
        use std::slice::from_raw_parts_mut;

        let n = self.len();
        assert!(n == plan.size, "the plan is not appropriate for the dataset");

        let h = n >> 1;
        if h == 0 {
            return;
        }

        let data = unsafe { from_raw_parts_mut(self.as_mut_ptr() as *mut c64, h) };
        match plan.operation {
            Operation::Forward => {
                data.transform(plan);
                compose(data, h, &plan.factors, false);
            },
            Operation::Backward | Operation::Inverse => {
                compose(data, h, &plan.factors, true);
                data.transform(plan);
            },
        }
    }
}

impl Transform for Vec<f64> {
    #[inline(always)]
    fn transform(&mut self, plan: &Plan) {
        Transform::transform(self as &mut [f64], plan)
    }
}

/// Unpack the result produced by the forward transform applied to real data.
///
/// The function decodes the result of an application of `Transform::transform`
/// with `Operation::Forward` to `[f64]`. See the top-level description of the
/// crate for further details.
pub fn unpack(data: &[f64]) -> Vec<c64> {
    let n = data.len();
    let h = n >> 1;
    assert!(n.is_power_of_two(), "the number of points should be a power of two");
    let mut cdata = Vec::with_capacity(n);
    unsafe { cdata.set_len(n) };
    cdata[0] = c64!(data[0], 0.0);
    if h == 0 {
        return cdata;
    }
    for i in 1..h {
        cdata[i] = c64!(data[2 * i], data[2 * i + 1]);
    }
    cdata[h] = c64!(data[1], 0.0);
    for i in (h + 1)..n {
        cdata[i] = cdata[n - i].conj();
    }
    cdata
}

#[inline(always)]
fn compose(data: &mut [c64], n: usize, factors: &[c64], inverse: bool) {
    let h = n >> 1;
    data[0] = c64!(data[0].re + data[0].im, data[0].re - data[0].im);
    if inverse {
        data[0] = data[0].scale(0.5);
    }
    let m = factors.len();
    let sign = if inverse { 1.0 } else { -1.0 };
    for i in 1..h {
        let j = n - i;
        let part1 = data[i] + data[j].conj();
        let part2 = data[i] - data[j].conj();
        let product = c64!(0.0, sign) * factors[m - j] * part2;
        data[i] = (part1 + product).scale(0.5);
        data[j] = (part1 - product).scale(0.5).conj();
    }
    data[h] = data[h].conj();
}

#[cfg(test)]
mod tests {
    #[test]
    fn unpack() {
        let data = (0..4).map(|i| (i + 1) as f64).collect::<Vec<_>>();
        assert_eq!(super::unpack(&data), vec![
            c64!(1.0, 0.0), c64!(3.0, 4.0), c64!(2.0, 0.0), c64!(3.0, -4.0),
        ]);

        let data = (0..8).map(|i| (i + 1) as f64).collect::<Vec<_>>();
        assert_eq!(super::unpack(&data), vec![
            c64!(1.0, 0.0), c64!(3.0, 4.0), c64!(5.0, 6.0), c64!(7.0, 8.0),
            c64!(2.0, 0.0), c64!(7.0, -8.0), c64!(5.0, -6.0), c64!(3.0, -4.0),
        ]);
    }
}
<commit_msg>real: make it work for two-point datasets<commit_after>use {Operation, Plan, Transform, c64};

impl Transform for [f64] {
    fn transform(&mut self, plan: &Plan) {
        use std::slice::from_raw_parts_mut;

        let n = self.len();
        assert!(n == plan.size, "the plan is not appropriate for the dataset");

        let h = n >> 1;
        if h == 0 {
            return;
        }

        let data = unsafe { from_raw_parts_mut(self.as_mut_ptr() as *mut c64, h) };
        match plan.operation {
            Operation::Forward => {
                data.transform(plan);
                compose(data, h, &plan.factors, false);
            },
            Operation::Backward | Operation::Inverse => {
                compose(data, h, &plan.factors, true);
                data.transform(plan);
            },
        }
    }
}

impl Transform for Vec<f64> {
    #[inline(always)]
    fn transform(&mut self, plan: &Plan) {
        Transform::transform(self as &mut [f64], plan)
    }
}

/// Unpack the result produced by the forward transform applied to real data.
///
/// The function decodes the result of an application of `Transform::transform`
/// with `Operation::Forward` to `[f64]`. See the top-level description of the
/// crate for further details.
pub fn unpack(data: &[f64]) -> Vec<c64> {
    let n = data.len();
    let h = n >> 1;
    assert!(n.is_power_of_two(), "the number of points should be a power of two");
    let mut cdata = Vec::with_capacity(n);
    unsafe { cdata.set_len(n) };
    cdata[0] = c64!(data[0], 0.0);
    if h == 0 {
        return cdata;
    }
    for i in 1..h {
        cdata[i] = c64!(data[2 * i], data[2 * i + 1]);
    }
    cdata[h] = c64!(data[1], 0.0);
    for i in (h + 1)..n {
        cdata[i] = cdata[n - i].conj();
    }
    cdata
}

#[inline(always)]
fn compose(data: &mut [c64], n: usize, factors: &[c64], inverse: bool) {
    let h = n >> 1;
    data[0] = c64!(data[0].re + data[0].im, data[0].re - data[0].im);
    if inverse {
        data[0] = data[0].scale(0.5);
    }
    if h == 0 {
        return;
    }
    let m = factors.len();
    let sign = if inverse { 1.0 } else { -1.0 };
    for i in 1..h {
        let j = n - i;
        let part1 = data[i] + data[j].conj();
        let part2 = data[i] - data[j].conj();
        let product = c64!(0.0, sign) * factors[m - j] * part2;
        data[i] = (part1 + product).scale(0.5);
        data[j] = (part1 - product).scale(0.5).conj();
    }
    data[h] = data[h].conj();
}

#[cfg(test)]
mod tests {
    #[test]
    fn unpack() {
        let data = (0..4).map(|i| (i + 1) as f64).collect::<Vec<_>>();
        assert_eq!(super::unpack(&data), vec![
            c64!(1.0, 0.0), c64!(3.0, 4.0), c64!(2.0, 0.0), c64!(3.0, -4.0),
        ]);

        let data = (0..8).map(|i| (i + 1) as f64).collect::<Vec<_>>();
        assert_eq!(super::unpack(&data), vec![
            c64!(1.0, 0.0), c64!(3.0, 4.0), c64!(5.0, 6.0), c64!(7.0, 8.0),
            c64!(2.0, 0.0), c64!(7.0, -8.0), c64!(5.0, -6.0), c64!(3.0, -4.0),
        ]);
    }
}
<|endoftext|>


<commit_before>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::{cleanup_dir,random_name};

static MUXED_NAME_STR: &'static str = "muxed";

/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(name: &Path) -> Path {
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create muxed directory: {}", path.filename())
    }

    path.clone()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

pub fn muxed_dir() -> Path {
    let path = muxed_path();
    match path.exists() {
        true  => path,
        false => create_muxed_dir(&path)
    }
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    cleanup_dir(muxed_path);
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    cleanup_dir(muxed_path);
}
<commit_msg>I can't lie. This was written a few days ago and I forget what lead to the changes.<commit_after>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::{cleanup_dir,random_name};

static MUXED_NAME_STR: &'static str = "muxed";

pub fn tester() { }
pub fn tester(path: Path) { }
/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(path: &Path) -> Path {
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create muxed directory: {}", path.filename())
    }

    path.clone()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

pub fn muxed_dir() -> Path {
    let path = muxed_path();
    match path.exists() {
        true  => path,
        false => create_muxed_dir(&path)
    }
}

#[cfg(test)] pub fn muxed_path() -> Path {
    Path::new(format!("/tmp/.muxed_{}/", random_name()))
}

#[cfg(not_test)] pub fn muxed_path() -> Path {
    Path::new(format!("{}/.{}/", root::homedir_string(), &MUXED_NAME_STR.to_string()))
}

#[test]
fn creates_muxed_dir() {
    let muxed_path = &muxed_path();
    create_muxed_dir(muxed_path);
    assert!(muxed_path.exists());
    cleanup_dir(muxed_path);
}

#[test]
fn muxed_dir_creates_dir() {
    let muxed_path = &muxed_dir();
    assert!(muxed_path.exists());
    cleanup_dir(muxed_path);
}
<|endoftext|>


<commit_before>//! Draw text

use crate::{
    character::CharacterCache,
    color,
    math::Matrix2d,
    types::{Color, FontSize},
    DrawState, Graphics, Image, Transformed,
};

/// Renders text
#[derive(Copy, Clone)]
pub struct Text {
    /// The color
    pub color: Color,
    /// The font size
    pub font_size: FontSize,
    /// Whether or not the text's position should be rounded (to a signed distance field).
    pub round: bool,
}

impl Text {
    /// Creates a new text with black color
    pub fn new(font_size: FontSize) -> Text {
        Text {
            color: color::BLACK,
            font_size,
            round: false,
        }
    }

    /// Creates a new colored text
    pub fn new_color(color: Color, font_size: FontSize) -> Text {
        Text {
            color,
            font_size,
            round: false,
        }
    }

    /// A builder method indicating that the Text's position should be rounded upon drawing.
    pub fn round(mut self) -> Text {
        self.round = true;
        self
    }

    /// Draws text with a character cache
    pub fn draw<C, G>(
        &self,
        text: &str,
        cache: &mut C,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G,
    ) -> Result<(), C::Error>
    where
        C: CharacterCache,
        G: Graphics<Texture = <C as CharacterCache>::Texture>,
    {
        let mut image = Image::new_color(self.color);

        let mut x = 0.0;
        let mut y = 0.0;
        for ch in text.chars() {
            let character = cache.character(self.font_size, ch)?;
            let mut ch_x = x + character.left();
            let mut ch_y = y - character.top();
            if self.round {
                ch_x = ch_x.round();
                ch_y = ch_y.round();
            }
            image = image.src_rect([
                character.atlas_offset[0],
                character.atlas_offset[1],
                character.atlas_size[0],
                character.atlas_size[1],
            ]);
            image.draw(
                character.texture,
                draw_state,
                transform.trans(ch_x, ch_y),
                g,
            );
            x += character.advance_width();
            y += character.advance_height();
        }

        Ok(())
    }
}
<commit_msg>Added `Text::draw_pos`<commit_after>//! Draw text

use crate::{
    character::CharacterCache,
    color,
    math::Matrix2d,
    math::Vec2d,
    types::{Color, FontSize},
    DrawState, Graphics, Image, Transformed,
};

/// Renders text
#[derive(Copy, Clone)]
pub struct Text {
    /// The color
    pub color: Color,
    /// The font size
    pub font_size: FontSize,
    /// Whether or not the text's position should be rounded (to a signed distance field).
    pub round: bool,
}

impl Text {
    /// Creates a new text with black color
    pub fn new(font_size: FontSize) -> Text {
        Text {
            color: color::BLACK,
            font_size,
            round: false,
        }
    }

    /// Creates a new colored text
    pub fn new_color(color: Color, font_size: FontSize) -> Text {
        Text {
            color,
            font_size,
            round: false,
        }
    }

    /// A builder method indicating that the Text's position should be rounded upon drawing.
    pub fn round(mut self) -> Text {
        self.round = true;
        self
    }

    /// Draws text at position with a character cache
    pub fn draw_pos<C, G>(
        &self,
        text: &str,
        pos: Vec2d,
        cache: &mut C,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G,
    ) -> Result<(), C::Error>
    where
        C: CharacterCache,
        G: Graphics<Texture = <C as CharacterCache>::Texture>,
    {
        self.draw(text, cache, draw_state, transform.trans_pos(pos), g)
    }

    /// Draws text with a character cache
    pub fn draw<C, G>(
        &self,
        text: &str,
        cache: &mut C,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G,
    ) -> Result<(), C::Error>
    where
        C: CharacterCache,
        G: Graphics<Texture = <C as CharacterCache>::Texture>,
    {
        let mut image = Image::new_color(self.color);

        let mut x = 0.0;
        let mut y = 0.0;
        for ch in text.chars() {
            let character = cache.character(self.font_size, ch)?;
            let mut ch_x = x + character.left();
            let mut ch_y = y - character.top();
            if self.round {
                ch_x = ch_x.round();
                ch_y = ch_y.round();
            }
            image = image.src_rect([
                character.atlas_offset[0],
                character.atlas_offset[1],
                character.atlas_size[0],
                character.atlas_size[1],
            ]);
            image.draw(
                character.texture,
                draw_state,
                transform.trans(ch_x, ch_y),
                g,
            );
            x += character.advance_width();
            y += character.advance_height();
        }

        Ok(())
    }
}
<|endoftext|>