'use babel';

import * as fs from 'fs';
import * as path from 'path';
import { BufferedProcess } from 'atom';
import config from './config';

const RUST_PANIC = 101;
const RUSTSYM_SEARCH_FLAGS = {
  EXTENDED_LOCAL: '',
  LOCAL: '-l',
  GLOBAL: '-g'
};

export default class SymbolSearcher {

  static get MODE() {
    return RUSTSYM_SEARCH_FLAGS;
  }

  constructor() {
    this.searchProcess = null;
  }

  search(basePath, filePath, query, searchMode) {
    if (this.searchProcess) {
      this.searchProcess.kill();
      this.searchProcess = null;
    }
    // Ensure executable exists if the user provides the setting
    let command = config.getExecutablePath();
    if (command && !fs.existsSync(command)) {
      return Promise.reject({
        message: 'Please check your \`rustsym\` executable path setting.',
        stack: `The executable path provided (\`${command}\`) does not exist on the file system.`
      });
    } else {
      command = 'rustsym';
    }
    // Must use base path when searching globally with rustsym
    if (searchMode === RUSTSYM_SEARCH_FLAGS.GLOBAL) {
      let crateRootPath = getCrateRoot(path.join(basePath, filePath));
      filePath = path.relative(basePath, crateRootPath);
    }
    return new Promise((resolve, reject) => {
      let data = null;
      let args = ['search', searchMode, filePath, query];
      let options = {
        cwd: basePath,
        // Needed to trigger error callback on Windows
        // https://github.com/atom/atom/issues/7793
        shell: false
      };
      let stdout = buffer => {
        // Only expect one JSON array of objects
        data = buffer;
      };
      let stderr = buffer => {
        data = buffer;
      };
      let exit = code => {
        if (code) {
          reject({
            message: code === RUST_PANIC
              ? `\`${command}\` panicked while searching \`${path.join(basePath, filePath)}\` for symbols.`
              : `\`${command}\` terminated abnormally with code \`${code}\`.`,
            stack: data
          });
        } else {
          try {
            let symbols = JSON.parse(data);
            resolve(symbols);
          } catch (err) {
            reject({
              message: `\`${command}\` produced malformed JSON symbol data.`,
              stack: data
            });
          }
        }
        this.searchProcess = null;
      };
      this.searchProcess = new BufferedProcess({
        command,
        args,
        options,
        stdout,
        stderr,
        exit
      }).onWillThrowError(err => {
        if (err.error.errno === 'ENOENT') {
          reject({
            message: `Unable to locate \`${command}\`.\n\nPlease ensure it is available in your \`PATH\`.`,
            stack: err.error.stack
          });
          err.handle();
        }
        this.searchProcess = null;
      });
    });
  }

}

function getCrateRoot(filePath) {
  let p = filePath;
  while (p !== '') {
    let index = p.lastIndexOf(path.sep);
    if (index === -1) break;
    p = p.slice(0, index);
    if (fs.existsSync(path.join(p, 'Cargo.toml'))) return p;
  }
}

@@ -681,12 +681,22 @@
 mand
- && 
+) %7B%0A      if (
 !fs.
@@ -720,24 +720,26 @@
 d)) %7B%0A      
+ 
+ 
 return Promi
@@ -742,32 +742,34 @@
 romise.reject(%7B%0A
+  
         message:
@@ -831,24 +831,26 @@
 .',%0A        
+ 
+ 
 stack: %60The 
@@ -929,28 +929,38 @@
 tem.%60%0A      
+  
 %7D);%0A
+      %7D%0A
     %7D else %7B

Fix #1: Executable Path in config is ignored