1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
extern crate racer_interner;
#[macro_use]
extern crate serde;
extern crate serde_json;

pub mod mapping;
pub mod metadata;

use metadata::Metadata;
use std::env;
use std::error::Error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::{self, Utf8Error};

#[derive(Debug)]
pub enum ErrorKind {
    Encode(Utf8Error),
    Json(serde_json::Error),
    Io(io::Error),
    Subprocess(String),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErrorKind::Encode(e) => fmt::Display::fmt(e, f),
            ErrorKind::Json(e) => fmt::Display::fmt(e, f),
            ErrorKind::Io(e) => fmt::Display::fmt(e, f),
            ErrorKind::Subprocess(s) => write!(f, "stderr: {}", s),
        }
    }
}

impl Error for ErrorKind {
    fn description(&self) -> &str {
        match self {
            ErrorKind::Encode(e) => e.description(),
            ErrorKind::Json(e) => e.description(),
            ErrorKind::Io(e) => e.description(),
            ErrorKind::Subprocess(s) => &s,
        }
    }
}

impl From<Utf8Error> for ErrorKind {
    fn from(e: Utf8Error) -> ErrorKind {
        ErrorKind::Encode(e)
    }
}

impl From<serde_json::Error> for ErrorKind {
    fn from(e: serde_json::Error) -> ErrorKind {
        ErrorKind::Json(e)
    }
}

impl From<io::Error> for ErrorKind {
    fn from(e: io::Error) -> ErrorKind {
        ErrorKind::Io(e)
    }
}

pub fn find_manifest(mut current: &Path) -> Option<PathBuf> {
    let file = "Cargo.toml";
    if current.is_dir() {
        let manifest = current.join(file);
        if manifest.exists() {
            return Some(manifest.to_owned());
        }
    }
    while let Some(parent) = current.parent() {
        let manifest = current.join(file);
        if manifest.exists() {
            return Some(manifest.to_owned());
        }
        current = parent;
    }
    None
}

pub fn run(manifest_path: &Path, frozen: bool) -> Result<Metadata, ErrorKind> {
    let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_owned());
    let mut cmd = Command::new(cargo);
    cmd.arg("metadata");
    cmd.arg("--all-features");
    cmd.args(&["--format-version", "1"]);
    cmd.args(&["--color", "never"]);
    cmd.arg("--manifest-path");
    cmd.arg(manifest_path.as_os_str());
    if frozen {
        cmd.arg("--frozen");
    }
    let op = cmd.output()?;
    if !op.status.success() {
        let stderr = String::from_utf8(op.stderr).map_err(|e| e.utf8_error())?;
        return Err(ErrorKind::Subprocess(stderr));
    }
    serde_json::from_slice(&op.stdout).map_err(From::from)
}