<commit_before>import std::{str, uint, int, vec};
import syntax::ast::*;
import syntax::ast_util::*;
import util::ppaux::fn_ident_to_string;
import std::option::*;
import syntax::visit;
import aux::*;
import std::map::new_int_hash;
import util::common::new_def_hash;
import syntax::codemap::span;
import syntax::ast_util::respan;

type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt};

fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) {
    for each p: @pat in pat_bindings(loc.node.pat) {
        let ident = alt p.node { pat_bind(id) { id } };
        log "collect_local: pushing " + ident;;
        *cx.cs += [respan(loc.span, ninit(p.id, ident))];
    }
    visit::visit_local(loc, cx, v);
}

fn collect_pred(e: @expr, cx: ctxt, v: visit::vt<ctxt>) {
    alt e.node {
      expr_check(_, ch) { *cx.cs += [expr_to_constr(cx.tcx, ch)]; }
      expr_if_check(ex, _, _) { *cx.cs += [expr_to_constr(cx.tcx, ex)]; }

      // If it's a call, generate appropriate instances of the
      // call's constraints.
      expr_call(operator, operands) {
        for c: @ty::constr in constraints_expr(cx.tcx, operator) {
            let ct: sp_constr =
                respan(c.span,
                       aux::substitute_constr_args(cx.tcx, operands, c));
            *cx.cs += [ct];
        }
      }
      _ { }
    }
    // visit subexpressions
    visit::visit_expr(e, cx, v);
}

fn find_locals(tcx: ty::ctxt, f: _fn, tps: [ty_param], sp: span, i: fn_ident,
               id: node_id) -> ctxt {
    let cx: ctxt = {cs: @mutable [], tcx: tcx};
    let visitor = visit::default_visitor::<ctxt>();

    visitor =
        @{visit_local: collect_local,
          visit_expr: collect_pred,
          visit_fn: do_nothing with *visitor};
    visit::visit_fn(f, tps, sp, i, id, cx, visit::mk_vt(visitor));
    ret cx;
}

fn add_constraint(tcx: ty::ctxt, c: sp_constr, next: uint, tbl: constr_map) ->
   uint {
    log constraint_to_str(tcx, c) + " |-> " + std::uint::str(next);
    alt c.node {
      ninit(id, i) { tbl.insert(local_def(id), cinit(next, c.span, i)); }
      npred(p, d_id, args) {
        alt tbl.find(d_id) {
          some(ct) {
            alt ct {
              cinit(_, _, _) {
                tcx.sess.bug("add_constraint: same def_id used" +
                                 " as a variable and a pred");
              }
              cpred(_, pds) {
                *pds += [respan(c.span, {args: args, bit_num: next})];
              }
            }
          }
          none. {
            let rslt: @mutable [pred_args] =
                @mutable [respan(c.span, {args: args, bit_num: next})];
            tbl.insert(d_id, cpred(p, rslt));
          }
        }
      }
    }
    ret next + 1u;
}

/* builds a table mapping each local var defined in f
   to a bit number in the precondition/postcondition vectors */
fn mk_fn_info(ccx: crate_ctxt, f: _fn, tp: [ty_param], f_sp: span,
              f_name: fn_ident, id: node_id) {
    let name = fn_ident_to_string(id, f_name);
    let res_map = @new_def_hash::<constraint>();
    let next: uint = 0u;

    let cx: ctxt = find_locals(ccx.tcx, f, tp, f_sp, f_name, id);
    /* now we have to add bit nums for both the constraints
       and the variables... */

    for c: sp_constr in { *cx.cs } {
        next = add_constraint(cx.tcx, c, next, res_map);
    }
    /* if this function has any constraints, instantiate them to the
       argument names and add them */
    let sc;
    for c: @constr in f.decl.constraints {
        sc = ast_constr_to_sp_constr(cx.tcx, f.decl.inputs, c);
        next = add_constraint(cx.tcx, sc, next, res_map);
    }

    /* Need to add constraints for args too, b/c they
    can be deinitialized */
    for a: arg in f.decl.inputs {
        next =
            add_constraint(cx.tcx, respan(f_sp, ninit(a.id, a.ident)), next,
                           res_map);
    }

    /* add the special i_diverge and i_return constraints
    (see the type definition for auxiliary::fn_info for an explanation) */

    // use the name of the function for the "return" constraint
    next =
        add_constraint(cx.tcx, respan(f_sp, ninit(id, name)), next, res_map);
    // and the name of the function, with a '!' appended to it, for the
    // "diverges" constraint
    let diverges_id = ccx.tcx.sess.next_node_id();
    let diverges_name = name + "!";
    add_constraint(cx.tcx, respan(f_sp, ninit(diverges_id, diverges_name)),
                   next, res_map);

    let v: @mutable [node_id] = @mutable [];
    let rslt =
        {constrs: res_map,

         // add 2 to account for the i_return and i_diverge constraints
         num_constraints:
             vec::len(*cx.cs) + vec::len(f.decl.constraints) +
                 vec::len(f.decl.inputs) + 2u,
         cf: f.decl.cf,
         i_return: ninit(id, name),
         i_diverge: ninit(diverges_id, diverges_name),
         used_vars: v};
    ccx.fm.insert(id, rslt);
    log name + " has " + std::uint::str(num_constraints(rslt)) +
            " constraints";
}

/* initializes the global fn_info_map (mapping each function ID, including
   nested locally defined functions, onto a mapping from local variable name
   to bit number) */
fn mk_f_to_fn_info(ccx: crate_ctxt, c: @crate) {
    let visitor =
        visit::mk_simple_visitor(@{visit_fn:
                                       bind mk_fn_info(ccx, _, _, _, _, _)
                                      with *visit::default_simple_visitor()});
    visit::visit_crate(*c, (), visitor);
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Add a workaround hack for issue #913<commit_after>import std::{str, uint, int, vec};
import syntax::ast::*;
import syntax::ast_util::*;
import util::ppaux::fn_ident_to_string;
import std::option::*;
import syntax::visit;
import aux::*;
import std::map::new_int_hash;
import util::common::new_def_hash;
import syntax::codemap::span;
import syntax::ast_util::respan;

type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt};

fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) {
    for each p: @pat in pat_bindings(loc.node.pat) {
        let ident = alt p.node { pat_bind(id) { id } };
        log "collect_local: pushing " + ident;;
        *cx.cs += [respan(loc.span, ninit(p.id, ident))];
    }
    visit::visit_local(loc, cx, v);
}

fn collect_pred(e: @expr, cx: ctxt, v: visit::vt<ctxt>) {
    alt e.node {
      expr_check(_, ch) { *cx.cs += [expr_to_constr(cx.tcx, ch)]; }
      expr_if_check(ex, _, _) { *cx.cs += [expr_to_constr(cx.tcx, ex)]; }

      // If it's a call, generate appropriate instances of the
      // call's constraints.
      expr_call(operator, operands) {
        for c: @ty::constr in constraints_expr(cx.tcx, operator) {
            let ct: sp_constr =
                respan(c.span,
                       aux::substitute_constr_args(cx.tcx, operands, c));
            *cx.cs += [ct];
        }
      }
      _ { }
    }
    // visit subexpressions
    visit::visit_expr(e, cx, v);
}

fn find_locals(tcx: ty::ctxt, f: _fn, tps: [ty_param], sp: span, i: fn_ident,
               id: node_id) -> ctxt {
    let cx: ctxt = {cs: @mutable [], tcx: tcx};
    let visitor = visit::default_visitor::<ctxt>();

    visitor =
        @{visit_local: collect_local,
          visit_expr: collect_pred,
          visit_fn: do_nothing with *visitor};
    visit::visit_fn(f, tps, sp, i, id, cx, visit::mk_vt(visitor));
    ret cx;
}

fn add_constraint(tcx: ty::ctxt, c: sp_constr, next: uint, tbl: constr_map) ->
   uint {
    log constraint_to_str(tcx, c) + " |-> " + std::uint::str(next);
    alt c.node {
      ninit(id, i) { tbl.insert(local_def(id), cinit(next, c.span, i)); }
      npred(p, d_id, args) {
        alt tbl.find(d_id) {
          some(ct) {
            alt ct {
              cinit(_, _, _) {
                tcx.sess.bug("add_constraint: same def_id used" +
                                 " as a variable and a pred");
              }
              cpred(_, pds) {
                *pds += [respan(c.span, {args: args, bit_num: next})];
              }
            }
          }
          none. {
            let rslt: @mutable [pred_args] =
                @mutable [respan(c.span, {args: args, bit_num: next})];
            tbl.insert(d_id, cpred(p, rslt));
          }
        }
      }
    }
    ret next + 1u;
}

/* builds a table mapping each local var defined in f
   to a bit number in the precondition/postcondition vectors */
fn mk_fn_info(ccx: crate_ctxt, f: _fn, tp: [ty_param], f_sp: span,
              f_name: fn_ident, id: node_id) {
    let name = fn_ident_to_string(id, f_name);
    let res_map = @new_def_hash::<constraint>();
    let next: uint = 0u;

    let cx: ctxt = find_locals(ccx.tcx, f, tp, f_sp, f_name, id);
    /* now we have to add bit nums for both the constraints
       and the variables... */

    for c: sp_constr in { *cx.cs } {
        next = add_constraint(cx.tcx, c, next, res_map);
    }
    /* if this function has any constraints, instantiate them to the
       argument names and add them */
    let sc;
    for c: @constr in f.decl.constraints {
        sc = ast_constr_to_sp_constr(cx.tcx, f.decl.inputs, c);
        next = add_constraint(cx.tcx, sc, next, res_map);
    }

    /* Need to add constraints for args too, b/c they
    can be deinitialized */
    for a: arg in f.decl.inputs {
        next =
            add_constraint(cx.tcx, respan(f_sp, ninit(a.id, a.ident)), next,
                           res_map);
    }

    /* add the special i_diverge and i_return constraints
    (see the type definition for auxiliary::fn_info for an explanation) */

    // use the name of the function for the "return" constraint
    next =
        add_constraint(cx.tcx, respan(f_sp, ninit(id, name)), next, res_map);
    // and the name of the function, with a '!' appended to it, for the
    // "diverges" constraint
    let diverges_id = ccx.tcx.sess.next_node_id();
    let diverges_name = name + "!";
    add_constraint(cx.tcx, respan(f_sp, ninit(diverges_id, diverges_name)),
                   next, res_map);

    let v: @mutable [node_id] = @mutable [];
    let rslt =
        {constrs: res_map,

         // add 2 to account for the i_return and i_diverge constraints
         // FIXME the 1u here is a kludge to make bug #913's impact somewhat
         // smaller. it should be removed once the bug is really fixed
         num_constraints:
             vec::len(*cx.cs) + vec::len(f.decl.constraints) +
                 vec::len(f.decl.inputs) + 2u + 1u,
         cf: f.decl.cf,
         i_return: ninit(id, name),
         i_diverge: ninit(diverges_id, diverges_name),
         used_vars: v};
    ccx.fm.insert(id, rslt);
    log name + " has " + std::uint::str(num_constraints(rslt)) +
            " constraints";
}

/* initializes the global fn_info_map (mapping each function ID, including
   nested locally defined functions, onto a mapping from local variable name
   to bit number) */
fn mk_f_to_fn_info(ccx: crate_ctxt, c: @crate) {
    let visitor =
        visit::mk_simple_visitor(@{visit_fn:
                                       bind mk_fn_info(ccx, _, _, _, _, _)
                                      with *visit::default_simple_visitor()});
    visit::visit_crate(*c, (), visitor);
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>import std::vec;
import std::vec::plus_option;

import front::ast;
import front::ast::crate;
import front::ast::ann;
import front::ast::arg;
import front::ast::method;
import front::ast::local;
import front::ast::item;
import front::ast::item_fn;
import front::ast::item_obj;
import front::ast::_obj;
import front::ast::obj_def_ids;
import front::ast::_fn;
import front::ast::ty_param;
import front::ast::_mod;
import front::ast::decl;
import front::ast::decl_local;
import front::ast::def_id;
import front::ast::ident;

import middle::walk::walk_crate;
import middle::walk::walk_fn;
import middle::walk::ast_visitor;

import aux::fn_info;
import aux::var_info;
import aux::crate_ctxt;

import util::common::new_def_hash;
import util::common::uistr;

fn var_is_local(def_id v, fn_info m) -> bool {
  ret (m.vars.contains_key(v));
}

fn collect_local(&@vec[tup(ident, def_id)] vars, &@decl d) -> () {
    alt (d.node) {
      case (decl_local(?loc)) {
        log("collect_local: pushing " + loc.ident);
        vec::push[tup(ident, def_id)](*vars, tup(loc.ident, loc.id));
      }
      case (_) { ret; }
    }
}

fn find_locals(&_fn f, &ident i, &def_id d) -> @vec[tup(ident,def_id)] {
  auto res = @vec::alloc[tup(ident,def_id)](0u);
  auto visitor = walk::default_visitor();
  visitor = rec(visit_decl_pre=bind collect_local(res,_) with visitor);
  walk_fn(visitor, f, i, d);
  ret res;
}

fn add_var(def_id v, ident nm, uint next, fn_info tbl) -> uint {
  log(nm + " |-> " + util::common::uistr(next));
  tbl.vars.insert(v, tup(next,nm));
  ret (next + 1u);
}

/* builds a table mapping each local var defined in f
   to a bit number in the precondition/postcondition vectors */
fn mk_fn_info(&_fn f, &def_id f_id, &ident f_name) -> fn_info {
    auto res = rec(vars=@new_def_hash[var_info](),
                   cf=f.decl.cf);
    let uint next = 0u;
    let vec[arg] f_args = f.decl.inputs;

    /* ignore args, which we know are initialized;
       just collect locally declared vars */

    let @vec[tup(ident,def_id)] locals = find_locals(f, f_name, f_id);
    // log (uistr(vec::len[tup(ident, def_id)](locals)) + " locals");
    for (tup(ident,def_id) p in *locals) {
        next = add_var(p._1, p._0, next, res);
    }
    /* add a pseudo-entry for the function's return value
       we can safely use the function's name itself for this purpose */
    add_var(f_id, f_name, next, res);

    log(f_name + " has " + uistr(vec::len[tup(ident, def_id)](*locals))
            + " locals");

    ret res;
}

/* FIXME: can do this with just one case -- for fn -- now */

/* extends mk_fn_info to an item, side-effecting the map fi from
   function IDs to fn_info maps
   only looks at function and object items. */
fn mk_fn_info_item (&crate_ctxt ccx, &@item i) -> () {
  alt (i.node) {
    case (item_fn(?i,?f,?ty_params,?id,?a)) {
      auto f_inf = mk_fn_info(f, id, i);
      ccx.fm.insert(id, f_inf);
    }
    case (item_obj(?i,?o,?ty_params,?odid,?a)) {
      auto all_methods = vec::clone[@method](o.methods);
      plus_option[@method](all_methods, o.dtor);
      auto f_inf;
      for (@method m in all_methods) {
        f_inf = mk_fn_info(m.node.meth, m.node.id, m.node.ident);
        ccx.fm.insert(m.node.id, f_inf);
      }
    }
    case (_) { ret; }
  }
}

/* initializes the global fn_info_map (mapping each function ID, including
   nested locally defined functions, onto a mapping from local variable name
   to bit number) */
fn mk_f_to_fn_info(&crate_ctxt ccx, @crate c) -> () {
  let ast_visitor vars_visitor = walk::default_visitor();
  vars_visitor = rec(visit_item_post=bind mk_fn_info_item(ccx,_)
                     with vars_visitor);

  walk_crate(vars_visitor, *c);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//

<commit_msg>tidy tstate.collect_locals<commit_after>import std::vec;
import std::vec::plus_option;

import front::ast;
import front::ast::crate;
import front::ast::ann;
import front::ast::arg;
import front::ast::method;
import front::ast::local;
import front::ast::item;
import front::ast::item_fn;
import front::ast::item_obj;
import front::ast::_obj;
import front::ast::obj_def_ids;
import front::ast::_fn;
import front::ast::ty_param;
import front::ast::_mod;
import front::ast::decl;
import front::ast::decl_local;
import front::ast::def_id;
import front::ast::ident;

import middle::walk::walk_crate;
import middle::walk::walk_fn;
import middle::walk::ast_visitor;

import aux::fn_info;
import aux::var_info;
import aux::crate_ctxt;

import util::common::new_def_hash;
import util::common::uistr;

fn var_is_local(def_id v, fn_info m) -> bool {
  ret (m.vars.contains_key(v));
}

fn collect_local(&@vec[tup(ident, def_id)] vars, &@decl d) -> () {
    alt (d.node) {
      case (decl_local(?loc)) {
        log("collect_local: pushing " + loc.ident);
        vec::push[tup(ident, def_id)](*vars, tup(loc.ident, loc.id));
      }
      case (_) { ret; }
    }
}

fn find_locals(&_fn f, &ident i, &def_id d) -> @vec[tup(ident,def_id)] {
  auto res = @vec::alloc[tup(ident,def_id)](0u);
  auto visitor = walk::default_visitor();
  visitor = rec(visit_decl_pre=bind collect_local(res,_) with visitor);
  walk_fn(visitor, f, i, d);
  ret res;
}

fn add_var(def_id v, ident nm, uint next, fn_info tbl) -> uint {
  log(nm + " |-> " + util::common::uistr(next));
  tbl.vars.insert(v, tup(next,nm));
  ret (next + 1u);
}

/* builds a table mapping each local var defined in f
   to a bit number in the precondition/postcondition vectors */
fn mk_fn_info(&crate_ctxt ccx, &_fn f, &ident f_name, &def_id f_id) -> () {
    auto res = rec(vars=@new_def_hash[var_info](),
                   cf=f.decl.cf);
    let uint next = 0u;
    let vec[arg] f_args = f.decl.inputs;

    /* ignore args, which we know are initialized;
       just collect locally declared vars */

    let @vec[tup(ident,def_id)] locals = find_locals(f, f_name, f_id);
    for (tup(ident,def_id) p in *locals) {
        next = add_var(p._1, p._0, next, res);
    }
    /* add a pseudo-entry for the function's return value
       we can safely use the function's name itself for this purpose */
    add_var(f_id, f_name, next, res);

    log(f_name + " has " + uistr(vec::len[tup(ident, def_id)](*locals))
            + " locals");

    ccx.fm.insert(f_id, res);
}

/* initializes the global fn_info_map (mapping each function ID, including
   nested locally defined functions, onto a mapping from local variable name
   to bit number) */
fn mk_f_to_fn_info(&crate_ctxt ccx, @crate c) -> () {
  let ast_visitor vars_visitor = walk::default_visitor();
  vars_visitor = rec(visit_fn_pre=bind mk_fn_info(ccx,_,_,_)
                     with vars_visitor);

  walk_crate(vars_visitor, *c);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//

<|endoftext|>


<commit_before>#![cfg(all(target_os = "linux", feature = "window"))]

use BuilderAttribs;
use ContextError;
use CreationError;
use GlContext;
use GlProfile;
use GlRequest;
use Api;
use PixelFormat;
use Robustness;

use libc;
use std::ffi::{CStr, CString};
use std::{mem, ptr, slice};

use api::x11::ffi;

use platform::Window as PlatformWindow;

pub struct Context {
    glx: ffi::glx::Glx,
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
    pixel_format: PixelFormat,
}

// TODO: remove me
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
    use std::ffi::CString;
    let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
    f(c_str.as_ptr())
}

impl Context {
    pub fn new<'a>(glx: ffi::glx::Glx, xlib: &ffi::Xlib, builder: &'a BuilderAttribs<'a>,
                   display: *mut ffi::Display)
                   -> Result<ContextPrototype<'a>, CreationError>
    {
        // finding the pixel format we want
        let (fb_config, pixel_format) = {
            let configs = unsafe { try!(enumerate_configs(&glx, xlib, display)) };
            try!(builder.choose_pixel_format(configs.into_iter()))
        };

        // getting the visual infos
        let visual_infos: ffi::glx::types::XVisualInfo = unsafe {
            let vi = glx.GetVisualFromFBConfig(display as *mut _, fb_config);
            if vi.is_null() {
                return Err(CreationError::OsError(format!("glxGetVisualFromFBConfig failed")));
            }
            let vi_copy = ptr::read(vi as *const _);
            (xlib.XFree)(vi as *mut _);
            vi_copy
        };

        Ok(ContextPrototype {
            glx: glx,
            builder: builder,
            display: display,
            fb_config: fb_config,
            visual_infos: unsafe { mem::transmute(visual_infos) },
            pixel_format: pixel_format,
        })
    }
}

impl GlContext for Context {
    unsafe fn make_current(&self) -> Result<(), ContextError> {
        // TODO: glutin needs some internal changes for proper error recovery
        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
        if res == 0 {
            panic!("glx::MakeCurrent failed");
        }
        Ok(())
    }

    fn is_current(&self) -> bool {
        unsafe { self.glx.GetCurrentContext() == self.context }
    }

    fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
        let addr = CString::new(addr.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        unsafe {
            self.glx.GetProcAddress(addr as *const _) as *const _
        }
    }

    fn swap_buffers(&self) -> Result<(), ContextError> {
        // TODO: glutin needs some internal changes for proper error recovery
        unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); }
        Ok(())
    }

    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            if self.is_current() {
                self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut());
            }

            self.glx.DestroyContext(self.display as *mut _, self.context);
        }
    }
}

pub struct ContextPrototype<'a> {
    glx: ffi::glx::Glx,
    builder: &'a BuilderAttribs<'a>,
    display: *mut ffi::Display,
    fb_config: ffi::glx::types::GLXFBConfig,
    visual_infos: ffi::XVisualInfo,
    pixel_format: PixelFormat,
}

impl<'a> ContextPrototype<'a> {
    pub fn get_visual_infos(&self) -> &ffi::XVisualInfo {
        &self.visual_infos
    }

    pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> {
        let share = if let Some(win) = self.builder.sharing {
            match win {
                &PlatformWindow::X(ref win) => match win.x.context {
                    ::api::x11::Context::Glx(ref c) => c.context,
                    _ => panic!("Cannot share contexts between different APIs")
                },
                _ => panic!("Cannot use glx on a non-X11 window.")
            }
        } else {
            ptr::null()
        };

        // loading the list of extensions
        let extensions = unsafe {
            let extensions = self.glx.QueryExtensionsString(self.display as *mut _, 0);     // FIXME: screen number
            let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
            String::from_utf8(extensions).unwrap()
        };

        // loading the extra GLX functions
        let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
            with_c_str(addr, |s| {
                unsafe { self.glx.GetProcAddress(s as *const u8) as *const _ }
            })
        });

        // creating GL context
        let context = match self.builder.gl_version {
            GlRequest::Latest => {
                if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions, (3, 2),
                                                 self.builder.gl_profile, self.builder.gl_debug,
                                                 self.builder.gl_robustness, share,
                                                 self.display, self.fb_config, &self.visual_infos)
                {
                    ctxt
                } else if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions,
                                                        (3, 1), self.builder.gl_profile,
                                                        self.builder.gl_debug,
                                                        self.builder.gl_robustness, share, self.display,
                                                        self.fb_config, &self.visual_infos)
                {
                    ctxt

                } else {
                    try!(create_context(&self.glx, &extra_functions, &extensions, (1, 0),
                                        self.builder.gl_profile, self.builder.gl_debug,
                                        self.builder.gl_robustness,
                                        share, self.display, self.fb_config, &self.visual_infos))
                }
            },
            GlRequest::Specific(Api::OpenGl, (major, minor)) => {
                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor),
                                    self.builder.gl_profile, self.builder.gl_debug,
                                    self.builder.gl_robustness, share, self.display, self.fb_config,
                                    &self.visual_infos))
            },
            GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
            GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor),
                                    self.builder.gl_profile, self.builder.gl_debug,
                                    self.builder.gl_robustness, share, self.display, self.fb_config,
                                    &self.visual_infos))
            },
        };

        // vsync
        if self.builder.vsync {
            unsafe { self.glx.MakeCurrent(self.display as *mut _, window, context) };

            if extra_functions.SwapIntervalEXT.is_loaded() {
                // this should be the most common extension
                unsafe {
                    extra_functions.SwapIntervalEXT(self.display as *mut _, window, 1);
                }

                // checking that it worked
                if self.builder.strict {
                    let mut swap = unsafe { mem::uninitialized() };
                    unsafe {
                        self.glx.QueryDrawable(self.display as *mut _, window,
                                               ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
                                               &mut swap);
                    }

                    if swap != 1 {
                        return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \
                                                    interval `1` but got `{}`", swap)));
                    }
                }

            // GLX_MESA_swap_control is not official
            /*} else if extra_functions.SwapIntervalMESA.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalMESA(1);
                }*/

            } else if extra_functions.SwapIntervalSGI.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalSGI(1);
                }

            } else if self.builder.strict {
                return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
            }

            unsafe { self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null()) };
        }

        Ok(Context {
            glx: self.glx,
            display: self.display,
            window: window,
            context: context,
            pixel_format: self.pixel_format,
        })
    }
}

fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,
                  version: (u8, u8), profile: Option<GlProfile>, debug: bool,
                  robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,
                  fb_config: ffi::glx::types::GLXFBConfig,
                  visual_infos: &ffi::XVisualInfo)
                  -> Result<ffi::GLXContext, CreationError>
{
    unsafe {
        let context = if extra_functions.CreateContextAttribsARB.is_loaded() {
            let mut attributes = Vec::with_capacity(9);

            attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
            attributes.push(version.0 as libc::c_int);
            attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
            attributes.push(version.1 as libc::c_int);

            if let Some(profile) = profile {
                let flag = match profile {
                    GlProfile::Compatibility =>
                        ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
                    GlProfile::Core =>
                        ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
                };

                attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int);
                attributes.push(flag as libc::c_int);
            }

            let flags = {
                let mut flags = 0;

                // robustness
                if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() {
                    match robustness {
                        Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
                            attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
                            attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int);
                            flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
                        },
                        Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
                            attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
                            attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int);
                            flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
                        },
                        Robustness::NotRobust => (),
                        Robustness::NoError => (),
                    }
                } else {
                    match robustness {
                        Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
                            return Err(CreationError::RobustnessNotSupported);
                        },
                        _ => ()
                    }
                }

                if debug {
                    flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int;
                }

                flags
            };

            attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
            attributes.push(flags);

            attributes.push(0);

            extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1,
                                                    attributes.as_ptr())

        } else {
            let visual_infos: *const ffi::XVisualInfo = visual_infos;
            glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1)
        };

        if context.is_null() {
            // TODO: check for errors and return `OpenGlVersionNotSupported`
            return Err(CreationError::OsError(format!("GL context creation failed")));
        }

        Ok(context)
    }
}

/// Enumerates all available FBConfigs
unsafe fn enumerate_configs(glx: &ffi::glx::Glx, xlib: &ffi::Xlib, display: *mut ffi::Display)
                            -> Result<Vec<(ffi::glx::types::GLXFBConfig, PixelFormat)>, CreationError>
{
    let configs = {
        let mut num_configs = 0;
        let vals = glx.GetFBConfigs(display as *mut _, 0, &mut num_configs);      // TODO: screen number
        assert!(!vals.is_null());
        let configs = slice::from_raw_parts(vals, num_configs as usize);
        (xlib.XFree)(vals as *mut _);
        configs.to_vec()
    };

    let get_attrib = |attrib: libc::c_int, fb_config: ffi::glx::types::GLXFBConfig| -> i32 {
        let mut value = 0;
        glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value);
        // TODO: check return value
        value
    };

    Ok(configs.into_iter().filter_map(|config| {
        let caveat = get_attrib(ffi::glx::CONFIG_CAVEAT as libc::c_int, config);
        if caveat == ffi::glx::NON_CONFORMANT_CONFIG as libc::c_int {
            return None;
        }

        // TODO: make sure everything is supported
        let pf = PixelFormat {
            hardware_accelerated: caveat == ffi::glx::NONE as libc::c_int,
            color_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int, config) as u8 +
                        get_attrib(ffi::glx::GREEN_SIZE as libc::c_int, config) as u8 +
                        get_attrib(ffi::glx::BLUE_SIZE as libc::c_int, config) as u8,
            alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as libc::c_int, config) as u8,
            depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as libc::c_int, config) as u8,
            stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as libc::c_int, config) as u8,
            stereoscopy: get_attrib(ffi::glx::STEREO as libc::c_int, config) != 0,
            double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as libc::c_int, config) != 0,
            multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as libc::c_int, config) != 0 {
                Some(get_attrib(ffi::glx::SAMPLES as libc::c_int, config) as u16)
            } else {
                None
            },
            srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int, config) != 0,
        };

        Some((config, pf))
    }).collect())
}
<commit_msg>Fix calling XFree before reading from memory<commit_after>#![cfg(all(target_os = "linux", feature = "window"))]

use BuilderAttribs;
use ContextError;
use CreationError;
use GlContext;
use GlProfile;
use GlRequest;
use Api;
use PixelFormat;
use Robustness;

use libc;
use std::ffi::{CStr, CString};
use std::{mem, ptr, slice};

use api::x11::ffi;

use platform::Window as PlatformWindow;

pub struct Context {
    glx: ffi::glx::Glx,
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
    pixel_format: PixelFormat,
}

// TODO: remove me
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
    use std::ffi::CString;
    let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
    f(c_str.as_ptr())
}

impl Context {
    pub fn new<'a>(glx: ffi::glx::Glx, xlib: &ffi::Xlib, builder: &'a BuilderAttribs<'a>,
                   display: *mut ffi::Display)
                   -> Result<ContextPrototype<'a>, CreationError>
    {
        // finding the pixel format we want
        let (fb_config, pixel_format) = {
            let configs = unsafe { try!(enumerate_configs(&glx, xlib, display)) };
            try!(builder.choose_pixel_format(configs.into_iter()))
        };

        // getting the visual infos
        let visual_infos: ffi::glx::types::XVisualInfo = unsafe {
            let vi = glx.GetVisualFromFBConfig(display as *mut _, fb_config);
            if vi.is_null() {
                return Err(CreationError::OsError(format!("glxGetVisualFromFBConfig failed")));
            }
            let vi_copy = ptr::read(vi as *const _);
            (xlib.XFree)(vi as *mut _);
            vi_copy
        };

        Ok(ContextPrototype {
            glx: glx,
            builder: builder,
            display: display,
            fb_config: fb_config,
            visual_infos: unsafe { mem::transmute(visual_infos) },
            pixel_format: pixel_format,
        })
    }
}

impl GlContext for Context {
    unsafe fn make_current(&self) -> Result<(), ContextError> {
        // TODO: glutin needs some internal changes for proper error recovery
        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
        if res == 0 {
            panic!("glx::MakeCurrent failed");
        }
        Ok(())
    }

    fn is_current(&self) -> bool {
        unsafe { self.glx.GetCurrentContext() == self.context }
    }

    fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
        let addr = CString::new(addr.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        unsafe {
            self.glx.GetProcAddress(addr as *const _) as *const _
        }
    }

    fn swap_buffers(&self) -> Result<(), ContextError> {
        // TODO: glutin needs some internal changes for proper error recovery
        unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); }
        Ok(())
    }

    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    fn get_pixel_format(&self) -> PixelFormat {
        self.pixel_format.clone()
    }
}

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            if self.is_current() {
                self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut());
            }

            self.glx.DestroyContext(self.display as *mut _, self.context);
        }
    }
}

pub struct ContextPrototype<'a> {
    glx: ffi::glx::Glx,
    builder: &'a BuilderAttribs<'a>,
    display: *mut ffi::Display,
    fb_config: ffi::glx::types::GLXFBConfig,
    visual_infos: ffi::XVisualInfo,
    pixel_format: PixelFormat,
}

impl<'a> ContextPrototype<'a> {
    pub fn get_visual_infos(&self) -> &ffi::XVisualInfo {
        &self.visual_infos
    }

    pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> {
        let share = if let Some(win) = self.builder.sharing {
            match win {
                &PlatformWindow::X(ref win) => match win.x.context {
                    ::api::x11::Context::Glx(ref c) => c.context,
                    _ => panic!("Cannot share contexts between different APIs")
                },
                _ => panic!("Cannot use glx on a non-X11 window.")
            }
        } else {
            ptr::null()
        };

        // loading the list of extensions
        let extensions = unsafe {
            let extensions = self.glx.QueryExtensionsString(self.display as *mut _, 0);     // FIXME: screen number
            let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();
            String::from_utf8(extensions).unwrap()
        };

        // loading the extra GLX functions
        let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
            with_c_str(addr, |s| {
                unsafe { self.glx.GetProcAddress(s as *const u8) as *const _ }
            })
        });

        // creating GL context
        let context = match self.builder.gl_version {
            GlRequest::Latest => {
                if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions, (3, 2),
                                                 self.builder.gl_profile, self.builder.gl_debug,
                                                 self.builder.gl_robustness, share,
                                                 self.display, self.fb_config, &self.visual_infos)
                {
                    ctxt
                } else if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions,
                                                        (3, 1), self.builder.gl_profile,
                                                        self.builder.gl_debug,
                                                        self.builder.gl_robustness, share, self.display,
                                                        self.fb_config, &self.visual_infos)
                {
                    ctxt

                } else {
                    try!(create_context(&self.glx, &extra_functions, &extensions, (1, 0),
                                        self.builder.gl_profile, self.builder.gl_debug,
                                        self.builder.gl_robustness,
                                        share, self.display, self.fb_config, &self.visual_infos))
                }
            },
            GlRequest::Specific(Api::OpenGl, (major, minor)) => {
                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor),
                                    self.builder.gl_profile, self.builder.gl_debug,
                                    self.builder.gl_robustness, share, self.display, self.fb_config,
                                    &self.visual_infos))
            },
            GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
            GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor),
                                    self.builder.gl_profile, self.builder.gl_debug,
                                    self.builder.gl_robustness, share, self.display, self.fb_config,
                                    &self.visual_infos))
            },
        };

        // vsync
        if self.builder.vsync {
            unsafe { self.glx.MakeCurrent(self.display as *mut _, window, context) };

            if extra_functions.SwapIntervalEXT.is_loaded() {
                // this should be the most common extension
                unsafe {
                    extra_functions.SwapIntervalEXT(self.display as *mut _, window, 1);
                }

                // checking that it worked
                if self.builder.strict {
                    let mut swap = unsafe { mem::uninitialized() };
                    unsafe {
                        self.glx.QueryDrawable(self.display as *mut _, window,
                                               ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
                                               &mut swap);
                    }

                    if swap != 1 {
                        return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \
                                                    interval `1` but got `{}`", swap)));
                    }
                }

            // GLX_MESA_swap_control is not official
            /*} else if extra_functions.SwapIntervalMESA.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalMESA(1);
                }*/

            } else if extra_functions.SwapIntervalSGI.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalSGI(1);
                }

            } else if self.builder.strict {
                return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
            }

            unsafe { self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null()) };
        }

        Ok(Context {
            glx: self.glx,
            display: self.display,
            window: window,
            context: context,
            pixel_format: self.pixel_format,
        })
    }
}

fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,
                  version: (u8, u8), profile: Option<GlProfile>, debug: bool,
                  robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,
                  fb_config: ffi::glx::types::GLXFBConfig,
                  visual_infos: &ffi::XVisualInfo)
                  -> Result<ffi::GLXContext, CreationError>
{
    unsafe {
        let context = if extra_functions.CreateContextAttribsARB.is_loaded() {
            let mut attributes = Vec::with_capacity(9);

            attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
            attributes.push(version.0 as libc::c_int);
            attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
            attributes.push(version.1 as libc::c_int);

            if let Some(profile) = profile {
                let flag = match profile {
                    GlProfile::Compatibility =>
                        ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
                    GlProfile::Core =>
                        ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
                };

                attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int);
                attributes.push(flag as libc::c_int);
            }

            let flags = {
                let mut flags = 0;

                // robustness
                if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() {
                    match robustness {
                        Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => {
                            attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
                            attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int);
                            flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
                        },
                        Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => {
                            attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int);
                            attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int);
                            flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int;
                        },
                        Robustness::NotRobust => (),
                        Robustness::NoError => (),
                    }
                } else {
                    match robustness {
                        Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => {
                            return Err(CreationError::RobustnessNotSupported);
                        },
                        _ => ()
                    }
                }

                if debug {
                    flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int;
                }

                flags
            };

            attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
            attributes.push(flags);

            attributes.push(0);

            extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1,
                                                    attributes.as_ptr())

        } else {
            let visual_infos: *const ffi::XVisualInfo = visual_infos;
            glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1)
        };

        if context.is_null() {
            // TODO: check for errors and return `OpenGlVersionNotSupported`
            return Err(CreationError::OsError(format!("GL context creation failed")));
        }

        Ok(context)
    }
}

/// Enumerates all available FBConfigs
unsafe fn enumerate_configs(glx: &ffi::glx::Glx, xlib: &ffi::Xlib, display: *mut ffi::Display)
                            -> Result<Vec<(ffi::glx::types::GLXFBConfig, PixelFormat)>, CreationError>
{
    let configs: Vec<ffi::glx::types::GLXFBConfig> = {
        let mut num_configs = 0;
        let vals = glx.GetFBConfigs(display as *mut _, 0, &mut num_configs);      // TODO: screen number
        assert!(!vals.is_null());
        let configs = slice::from_raw_parts(vals, num_configs as usize);
        let ret = configs.to_vec();
        (xlib.XFree)(vals as *mut _);
        ret
    };

    let get_attrib = |attrib: libc::c_int, fb_config: ffi::glx::types::GLXFBConfig| -> i32 {
        let mut value = 0;
        glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value);
        // TODO: check return value
        value
    };

    Ok(configs.into_iter().filter_map(|config| {
        let caveat = get_attrib(ffi::glx::CONFIG_CAVEAT as libc::c_int, config);
        if caveat == ffi::glx::NON_CONFORMANT_CONFIG as libc::c_int {
            return None;
        }

        // TODO: make sure everything is supported
        let pf = PixelFormat {
            hardware_accelerated: caveat == ffi::glx::NONE as libc::c_int,
            color_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int, config) as u8 +
                        get_attrib(ffi::glx::GREEN_SIZE as libc::c_int, config) as u8 +
                        get_attrib(ffi::glx::BLUE_SIZE as libc::c_int, config) as u8,
            alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as libc::c_int, config) as u8,
            depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as libc::c_int, config) as u8,
            stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as libc::c_int, config) as u8,
            stereoscopy: get_attrib(ffi::glx::STEREO as libc::c_int, config) != 0,
            double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as libc::c_int, config) != 0,
            multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as libc::c_int, config) != 0 {
                Some(get_attrib(ffi::glx::SAMPLES as libc::c_int, config) as u16)
            } else {
                None
            },
            srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int, config) != 0,
        };

        Some((config, pf))
    }).collect())
}
<|endoftext|>


<commit_before>/*
    Copyright (c) 2015, Alex Frappier Lachapelle
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

use std::env;
use std::error::Error;
use std::fs;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::exit;

extern crate clap;
use clap::{Arg, App, AppSettings, SubCommand};

extern crate hyper;

extern crate serde;
extern crate serde_json;

extern crate term;

mod appveyor;
mod config;
mod travis_ci;
mod trello;
mod utils;

////////////////////////////////////////////////////////////
//                         Macros                         //
////////////////////////////////////////////////////////////

include!("utils_macros.rs");

////////////////////////////////////////////////////////////
//                      Structs/Enums                     //
////////////////////////////////////////////////////////////

pub enum RuntimeMode {
    Generate,
    Push
}

pub enum ConfigMode {
    None,
    Default,
    Custom
}

pub enum OutputMode {
    Default,
    Custom
}

pub struct GenConfig {
    pub config_mode:      ConfigMode,
    pub output_mode:      OutputMode,
    pub config_file_path: PathBuf,
    pub output_direcrory: PathBuf
}

pub struct PushConfig {
    pub cli_op_trello_api_token:         String,
    pub cli_op_trello_api_list_id:       String,
    pub cli_op_trello_api_build_pass_id: String,
    pub cli_op_trello_api_build_fail_id: String,
    pub card_title:                      String,
    pub card_desc:                       String,
    pub compress_dir:                    String
}

////////////////////////////////////////////////////////////
//                          Impls                         //
////////////////////////////////////////////////////////////

impl RuntimeMode {
    pub fn new(is_push_mode: &bool) -> RuntimeMode {
        if *is_push_mode {
            RuntimeMode::Push
        } else {
            RuntimeMode::Generate
        }
    }
}

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

pub fn file_path_validator(file_path: String) -> Result<(), String> {
    match OpenOptions::new().read(true).write(true).create(true).open(Path::new(&file_path)) {
        Ok(_)  => Ok(()),
        Err(err) => {
            let mut err_string = "Cannot open file \"".to_string();
            err_string.push_str(file_path.as_str());
            err_string.push_str("\" due to an error: \"");
            err_string.push_str(err.description());
            err_string.push_str("\"");
            Err(err_string)
        }
    }
}

pub fn dir_path_validator(dir_path: String) -> Result<(), String> {

    //Check if it's even a directory.
    match fs::metadata(&dir_path) {
        Ok(metadata) => {
            if !metadata.is_dir() {
                let mut err_string = "\"".to_string();
                err_string.push_str(dir_path.as_str());
                err_string.push_str("\" is not a directory.");
                return Err(err_string);
            }
        },
        Err(err)     => {
            let mut err_string = "Failed to acquire metadata for \"".to_string();
            err_string.push_str(dir_path.as_str());
            err_string.push_str("\", do you have permission to write to this directory?");
            return Err(err_string);
        }
    }

    //Test if we can write a file to this directory
    let mut counter = 0;
    loop {
        //Generate a file name.
        let mut tmp_file_path:     String = dir_path.clone();
        let     tmp_file_name:     String = format!("ab{}ba.tmp", counter);
        let mut tmp_file_path_buf: PathBuf;
        tmp_file_path.push_str(tmp_file_name.as_str());
        tmp_file_path_buf = PathBuf::from(&tmp_file_path);

        //If file does not exist, check if we can create it with r/w permissions.
        if !tmp_file_path_buf.exists() {
            match OpenOptions::new().read(true).write(true).create(true).open(&tmp_file_path_buf.as_path()) {
                Ok(_)    => {
                    match fs::remove_file(&tmp_file_path_buf) {
                        Ok(_)  => return Ok(()),
                        Err(err) => {
                            let mut err_string = "Failed to delete temporary file: \"".to_string();
                            err_string.push_str(&tmp_file_path);
                            err_string.push_str("\"");
                            return Err(err_string);
                        }
                    }
                }
                Err(err) => {
                    let mut err_string = "Invalid output directory: \"".to_string();
                    err_string.push_str(err.description());
                    err_string.push_str("\"");
                    return Err(err_string);
                }
            }
        }
        counter += 1;
    }
}

fn main() {

    let     trellobst_version = "2.0.0-dev";
    let mut term              = term::stdout().unwrap();

    writeln_green!(term, "╔══════════════════════════════════════════════════════════╗");
    writeln_green!(term, "║         Welcome to the Trello Build Status Tool.         ║");
    writeln_green!(term, "╚══════════════════════════════════════════════════════════╝");

    ////////////////////////////////////////////////////////////
    //               Parse command line options               //
    ////////////////////////////////////////////////////////////

    let mut config_file_path = PathBuf::new();
    let mut output_direcrory = PathBuf::new();

    let matches = App::new("TrelloBST")
        .version(trellobst_version)
        .setting(AppSettings::SubcommandsNegateReqs)
        .subcommand(SubCommand::with_name("PUSH")
            .about("Pushes a build status to a trello board")
            .arg(Arg::with_name("CARD_TITLE")
                .short("t")
                .long("title")
                .help("Sets the title of the card.")
                .takes_value(true)
                .required(true))
            .arg(Arg::with_name("CARD_DESC")
                 .short("d")
                 .long("description")
                 .help("Sets the description of the card.")
                 .takes_value(true)
                 .required(true))
            .arg(Arg::with_name("TRELLO_API_TOKEN")
                 .short("T")
                 .long("token")
                 .help("Manually overrides the trello api token from the \"TRELLO_API_TOKEN\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_LIST_ID")
                 .short("L")
                 .long("list-id")
                 .help("Manually overrides the trello list id from the \"TRELLO_API_LIST_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_BUILD_PASS_ID")
                 .short("P")
                 .long("pass-id")
                 .help("Manually overrides the trello build pass id from the \"TRELLO_API_BUILD_PASS_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_BUILD_FAIL_ID")
                 .short("F")
                 .long("fail-id")
                 .help("Manually overrides the trello build fail id from the \"TRELLO_API_FAIL_PASS_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
        )
        .arg(Arg::with_name("CONFIG")
            .conflicts_with("NO-CONFIG")
            .short("c")
            .long("config")
            .help("Sets a custom TrelloBST configuration file.")
            .validator(file_path_validator)
            .takes_value(true))
        .arg(Arg::with_name("NO-CONFIG")
            .conflicts_with("CONFIG")
            .short("n")
            .long("no-config")
            .help("Won't use a configuration file for TrelloBST.")
            .takes_value(false))
        .arg(Arg::with_name("OUTPUT_DIR")
            .short("o")
            .long("output")
            .help("Sets the output directory for the CI configuration file.")
            .validator(dir_path_validator)
            .takes_value(true))
        .get_matches();

    let runtime_mode: RuntimeMode = RuntimeMode::new(&matches.is_present("PUSH"));
    match runtime_mode {
        Push => {
            //TODO: Push
        },
        Generate => {
            //TODO: Generate
        }
    }

    //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    if matches.is_present("CONFIG") {
        config_file_path = PathBuf::from(matches.value_of("CONFIG").unwrap());
        let status = utils::StatusPrint::from_string(&mut term, format!("Reading/Creating the configuration file at {}", config_file_path.to_str().unwrap()));
        if !utils::is_valid_file_path(&config_file_path) {
            status.error(&mut term);
            writeln_red!(term, "Error: Please enter a valid path for the output file. (Including read/write permissions.)");
            exit(-1);
        }
        status.success(&mut term);
    } else if !matches.is_present("NO-CONFIG") {

        let is_read_create_success: bool;
        let status = utils::StatusPrint::from_str(&mut term, "Reading/Creating the configuration file at the default location.");
        match env::home_dir() {
            Some(home_dir) => {
                config_file_path = home_dir;
                config_file_path.push(".TrelloBST.cfg");
                if utils::is_valid_file_path(&config_file_path) {
                    status.success(&mut term);
                    is_read_create_success = true;
                } else {
                    status.error(&mut term);
                    is_read_create_success = false;
                }
            }
            None           => {
                status.error(&mut term);
                writeln_red!(term, "Error: Failed to acquire the home directory.");
                is_read_create_success = false;
            }
        }

        if !is_read_create_success {
            writeln_red!(term, "Error: Reading/Creating configuration file at default location failed. Falling back to ./.TrelloBST.cfg");
            let status = utils::StatusPrint::from_str(&mut term, "Reading/Creating the configuration file at ./.TrelloBST.cfg");
            config_file_path = PathBuf::from("./.TrelloBST.cfg".to_string());
            if utils::is_valid_file_path(&config_file_path) {
                status.success(&mut term);
            } else {
                status.error(&mut term);
                writeln_red!(term, "Error: Reading/Creating configuration file at ./.TrelloBST.cfg failed.");
                exit(-2);
            }
        }
    }

    if matches.is_present("OUTPUT_DIR") {
        output_direcrory = PathBuf::from(matches.value_of("OUTPUT_DIR").unwrap());
        if !utils::is_valid_dir(&mut term, &output_direcrory) {
            writeln_red!(term, "Error: Please enter a valid path for the output directory. (Including read/write permissions.)");
            exit(-1);
        }
    } else {
        output_direcrory = PathBuf::from("./");
        if !utils::is_valid_dir(&mut term, &output_direcrory) {
            writeln_red!(term, "Error: Current directory is invalid. (Needs read/write permissions.)");
            exit(-1);
        }
    }

    ////////////////////////////////////////////////////////////
    //                     Parse config                       //
    ////////////////////////////////////////////////////////////

    let mut config = config::TrelloBSTAPIConfig::new();

    if config_file_path != PathBuf::new() {
        let status = utils::StatusPrint::from_str(&mut term, "Parsing the configuration file.");
        match config::TrelloBSTAPIConfig::from_file(&config_file_path) {
            Ok(_config) => {
                config = _config;
                status.success(&mut term);
            }
            Err(err)    => {
                status.error(&mut term);
                writeln_red!(term, "An error occurred: {}", err);
                writeln_red!(term, "Configuration file won't be used...");
                config_file_path = PathBuf::new()
            }
        }
    }

    ////////////////////////////////////////////////////////////
    //                   Setup Trello API                     //
    ////////////////////////////////////////////////////////////

    trello::setup_api(&mut term, &mut config);

    if config_file_path != PathBuf::new() {
        match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
            Ok(_)    => (),
            Err(err) => {
                writeln_red!(term, "Error: {}", err);
                writeln_red!(term, "Configuration file won't be used...");
                config_file_path = PathBuf::new()
            }
        }
    }

    ////////////////////////////////////////////////////////////
    //                  Setup Trello Board                    //
    ////////////////////////////////////////////////////////////

    let mut board_info = trello::TrelloBoardInfo::new();

    let is_board_created = trello::setup_board(&mut term,  &mut config, &mut board_info);
                           trello::setup_list(&mut term,   &mut config, &mut board_info, &is_board_created);
                           trello::setup_labels(&mut term, &mut config, &mut board_info, &is_board_created);

    ////////////////////////////////////////////////////////////
    //               Setup Travis-CI/Appveyor                 //
    ////////////////////////////////////////////////////////////

    loop {

        //Print options
        println!("For which continuous integration service do you want a configuration file for?");
        println!("[1] Travis-CI");
        println!("[2] AppVeyor");
        writeln_red!(term, "[0] Quit.");

        let mut option: usize = 0;
        loop {
            get_input_usize!(term, &mut option, "Please enter an option: ");
            if option <= 3 {
                break;
            }else {
                writeln_red!(term, "Please enter a valid option.");
            }
        }

        match option {
            1 => {

                //Get access token / API key
                match travis_ci::setup_api(&mut term, &mut config_file_path, &mut config) {
                    Ok(_)    => (),
                    Err(err) => {
                        writeln_red!(term, "Error setting up the travis-CI API token: {}", err);
                    }
                }

                //Save access token.
                if config_file_path != PathBuf::new() {
                    match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
                        Ok(_)    => (),
                        Err(err) => {
                            writeln_red!(term, "Error: {}", err);
                            writeln_red!(term, "Configuration file won't be used...");
                            config_file_path = PathBuf::new();
                        }
                    }
                }

                match travis_ci::create_travis_yml(&mut term, &config, &mut board_info, &output_direcrory) {
                    Ok(())   => (),
                    Err(err) => {
                        writeln_red!(term, "Error {}", err);
                    }
                }
            },
            2 => {

                //Get access token / API key
                appveyor::setup_api(&mut term, &mut config);

                //Save access token.
                if config_file_path != PathBuf::new() {
                    match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
                        Ok(_)    => (),
                        Err(err) => {
                            writeln_red!(term, "Error: {}", err);
                            writeln_red!(term, "Configuration file won't be used...");
                            config_file_path = PathBuf::new();
                        }
                    }
                }

                //Create appveyor.yml
                match appveyor::create_appveyor_yml(&mut term, &config, &mut board_info, &output_direcrory) {
                    Ok(()) => (),
                    Err(err) => {writeln_red!(term, "Error {}", err);}
                }
            },
            0 => exit(0),
            _ => {panic!("An invalid option slipped through...");}
        }
    }
}
<commit_msg>Fixed compile error.<commit_after>/*
    Copyright (c) 2015, Alex Frappier Lachapelle
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

use std::env;
use std::error::Error;
use std::fs;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::exit;

extern crate clap;
use clap::{Arg, App, AppSettings, SubCommand};

extern crate hyper;

extern crate serde;
extern crate serde_json;

extern crate term;

mod appveyor;
mod config;
mod travis_ci;
mod trello;
mod utils;

////////////////////////////////////////////////////////////
//                         Macros                         //
////////////////////////////////////////////////////////////

include!("utils_macros.rs");

////////////////////////////////////////////////////////////
//                      Structs/Enums                     //
////////////////////////////////////////////////////////////

pub enum RuntimeMode {
    Generate,
    Push
}

pub enum ConfigMode {
    None,
    Default,
    Custom
}

pub enum OutputMode {
    Default,
    Custom
}

pub struct GenConfig {
    pub config_mode:      ConfigMode,
    pub output_mode:      OutputMode,
    pub config_file_path: PathBuf,
    pub output_direcrory: PathBuf
}

pub struct PushConfig {
    pub cli_op_trello_api_token:         String,
    pub cli_op_trello_api_list_id:       String,
    pub cli_op_trello_api_build_pass_id: String,
    pub cli_op_trello_api_build_fail_id: String,
    pub card_title:                      String,
    pub card_desc:                       String,
    pub compress_dir:                    String
}

////////////////////////////////////////////////////////////
//                          Impls                         //
////////////////////////////////////////////////////////////

impl RuntimeMode {
    pub fn new(is_push_mode: &bool) -> RuntimeMode {
        if *is_push_mode {
            RuntimeMode::Push
        } else {
            RuntimeMode::Generate
        }
    }
}

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

pub fn file_path_validator(file_path: String) -> Result<(), String> {
    match OpenOptions::new().read(true).write(true).create(true).open(Path::new(&file_path)) {
        Ok(_)  => Ok(()),
        Err(err) => {
            let mut err_string = "Cannot open file \"".to_string();
            err_string.push_str(file_path.as_str());
            err_string.push_str("\" due to an error: \"");
            err_string.push_str(err.description());
            err_string.push_str("\"");
            Err(err_string)
        }
    }
}

pub fn dir_path_validator(dir_path: String) -> Result<(), String> {

    //Check if it's even a directory.
    match fs::metadata(&dir_path) {
        Ok(metadata) => {
            if !metadata.is_dir() {
                let mut err_string = "\"".to_string();
                err_string.push_str(dir_path.as_str());
                err_string.push_str("\" is not a directory.");
                return Err(err_string);
            }
        },
        Err(err)     => {
            let mut err_string = "Failed to acquire metadata for \"".to_string();
            err_string.push_str(dir_path.as_str());
            err_string.push_str("\", do you have permission to write to this directory?");
            return Err(err_string);
        }
    }

    //Test if we can write a file to this directory
    let mut counter = 0;
    loop {
        //Generate a file name.
        let mut tmp_file_path:     String = dir_path.clone();
        let     tmp_file_name:     String = format!("ab{}ba.tmp", counter);
        let mut tmp_file_path_buf: PathBuf;
        tmp_file_path.push_str(tmp_file_name.as_str());
        tmp_file_path_buf = PathBuf::from(&tmp_file_path);

        //If file does not exist, check if we can create it with r/w permissions.
        if !tmp_file_path_buf.exists() {
            match OpenOptions::new().read(true).write(true).create(true).open(&tmp_file_path_buf.as_path()) {
                Ok(_)    => {
                    match fs::remove_file(&tmp_file_path_buf) {
                        Ok(_)  => return Ok(()),
                        Err(err) => {
                            let mut err_string = "Failed to delete temporary file: \"".to_string();
                            err_string.push_str(&tmp_file_path);
                            err_string.push_str("\"");
                            return Err(err_string);
                        }
                    }
                }
                Err(err) => {
                    let mut err_string = "Invalid output directory: \"".to_string();
                    err_string.push_str(err.description());
                    err_string.push_str("\"");
                    return Err(err_string);
                }
            }
        }
        counter += 1;
    }
}

fn main() {

    let     trellobst_version = "2.0.0-dev";
    let mut term              = term::stdout().unwrap();

    writeln_green!(term, "╔══════════════════════════════════════════════════════════╗");
    writeln_green!(term, "║         Welcome to the Trello Build Status Tool.         ║");
    writeln_green!(term, "╚══════════════════════════════════════════════════════════╝");

    ////////////////////////////////////////////////////////////
    //               Parse command line options               //
    ////////////////////////////////////////////////////////////

    let mut config_file_path = PathBuf::new();
    let mut output_direcrory = PathBuf::new();

    let matches = App::new("TrelloBST")
        .version(trellobst_version)
        .setting(AppSettings::SubcommandsNegateReqs)
        .subcommand(SubCommand::with_name("PUSH")
            .about("Pushes a build status to a trello board")
            .arg(Arg::with_name("CARD_TITLE")
                .short("t")
                .long("title")
                .help("Sets the title of the card.")
                .takes_value(true)
                .required(true))
            .arg(Arg::with_name("CARD_DESC")
                 .short("d")
                 .long("description")
                 .help("Sets the description of the card.")
                 .takes_value(true)
                 .required(true))
            .arg(Arg::with_name("TRELLO_API_TOKEN")
                 .short("T")
                 .long("token")
                 .help("Manually overrides the trello api token from the \"TRELLO_API_TOKEN\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_LIST_ID")
                 .short("L")
                 .long("list-id")
                 .help("Manually overrides the trello list id from the \"TRELLO_API_LIST_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_BUILD_PASS_ID")
                 .short("P")
                 .long("pass-id")
                 .help("Manually overrides the trello build pass id from the \"TRELLO_API_BUILD_PASS_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
            .arg(Arg::with_name("TRELLO_BUILD_FAIL_ID")
                 .short("F")
                 .long("fail-id")
                 .help("Manually overrides the trello build fail id from the \"TRELLO_API_FAIL_PASS_ID\" environment variable.")
                 .takes_value(true)
                 .required(false))
        )
        .arg(Arg::with_name("CONFIG")
            .conflicts_with("NO-CONFIG")
            .short("c")
            .long("config")
            .help("Sets a custom TrelloBST configuration file.")
            .validator(file_path_validator)
            .takes_value(true))
        .arg(Arg::with_name("NO-CONFIG")
            .conflicts_with("CONFIG")
            .short("n")
            .long("no-config")
            .help("Won't use a configuration file for TrelloBST.")
            .takes_value(false))
        .arg(Arg::with_name("OUTPUT_DIR")
            .short("o")
            .long("output")
            .help("Sets the output directory for the CI configuration file.")
            .validator(dir_path_validator)
            .takes_value(true))
        .get_matches();

    let runtime_mode: RuntimeMode = RuntimeMode::new(&matches.is_present("PUSH"));
    match runtime_mode {
        RuntimeMode::Push => {
            //TODO: Push
        },
        RuntimeMode::Generate => {
            //TODO: Generate
        }
    }

    //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    if matches.is_present("CONFIG") {
        config_file_path = PathBuf::from(matches.value_of("CONFIG").unwrap());
        let status = utils::StatusPrint::from_string(&mut term, format!("Reading/Creating the configuration file at {}", config_file_path.to_str().unwrap()));
        if !utils::is_valid_file_path(&config_file_path) {
            status.error(&mut term);
            writeln_red!(term, "Error: Please enter a valid path for the output file. (Including read/write permissions.)");
            exit(-1);
        }
        status.success(&mut term);
    } else if !matches.is_present("NO-CONFIG") {

        let is_read_create_success: bool;
        let status = utils::StatusPrint::from_str(&mut term, "Reading/Creating the configuration file at the default location.");
        match env::home_dir() {
            Some(home_dir) => {
                config_file_path = home_dir;
                config_file_path.push(".TrelloBST.cfg");
                if utils::is_valid_file_path(&config_file_path) {
                    status.success(&mut term);
                    is_read_create_success = true;
                } else {
                    status.error(&mut term);
                    is_read_create_success = false;
                }
            }
            None           => {
                status.error(&mut term);
                writeln_red!(term, "Error: Failed to acquire the home directory.");
                is_read_create_success = false;
            }
        }

        if !is_read_create_success {
            writeln_red!(term, "Error: Reading/Creating configuration file at default location failed. Falling back to ./.TrelloBST.cfg");
            let status = utils::StatusPrint::from_str(&mut term, "Reading/Creating the configuration file at ./.TrelloBST.cfg");
            config_file_path = PathBuf::from("./.TrelloBST.cfg".to_string());
            if utils::is_valid_file_path(&config_file_path) {
                status.success(&mut term);
            } else {
                status.error(&mut term);
                writeln_red!(term, "Error: Reading/Creating configuration file at ./.TrelloBST.cfg failed.");
                exit(-2);
            }
        }
    }

    if matches.is_present("OUTPUT_DIR") {
        output_direcrory = PathBuf::from(matches.value_of("OUTPUT_DIR").unwrap());
        if !utils::is_valid_dir(&mut term, &output_direcrory) {
            writeln_red!(term, "Error: Please enter a valid path for the output directory. (Including read/write permissions.)");
            exit(-1);
        }
    } else {
        output_direcrory = PathBuf::from("./");
        if !utils::is_valid_dir(&mut term, &output_direcrory) {
            writeln_red!(term, "Error: Current directory is invalid. (Needs read/write permissions.)");
            exit(-1);
        }
    }

    ////////////////////////////////////////////////////////////
    //                     Parse config                       //
    ////////////////////////////////////////////////////////////

    let mut config = config::TrelloBSTAPIConfig::new();

    if config_file_path != PathBuf::new() {
        let status = utils::StatusPrint::from_str(&mut term, "Parsing the configuration file.");
        match config::TrelloBSTAPIConfig::from_file(&config_file_path) {
            Ok(_config) => {
                config = _config;
                status.success(&mut term);
            }
            Err(err)    => {
                status.error(&mut term);
                writeln_red!(term, "An error occurred: {}", err);
                writeln_red!(term, "Configuration file won't be used...");
                config_file_path = PathBuf::new()
            }
        }
    }

    ////////////////////////////////////////////////////////////
    //                   Setup Trello API                     //
    ////////////////////////////////////////////////////////////

    trello::setup_api(&mut term, &mut config);

    if config_file_path != PathBuf::new() {
        match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
            Ok(_)    => (),
            Err(err) => {
                writeln_red!(term, "Error: {}", err);
                writeln_red!(term, "Configuration file won't be used...");
                config_file_path = PathBuf::new()
            }
        }
    }

    ////////////////////////////////////////////////////////////
    //                  Setup Trello Board                    //
    ////////////////////////////////////////////////////////////

    let mut board_info = trello::TrelloBoardInfo::new();

    let is_board_created = trello::setup_board(&mut term,  &mut config, &mut board_info);
                           trello::setup_list(&mut term,   &mut config, &mut board_info, &is_board_created);
                           trello::setup_labels(&mut term, &mut config, &mut board_info, &is_board_created);

    ////////////////////////////////////////////////////////////
    //               Setup Travis-CI/Appveyor                 //
    ////////////////////////////////////////////////////////////

    loop {

        //Print options
        println!("For which continuous integration service do you want a configuration file for?");
        println!("[1] Travis-CI");
        println!("[2] AppVeyor");
        writeln_red!(term, "[0] Quit.");

        let mut option: usize = 0;
        loop {
            get_input_usize!(term, &mut option, "Please enter an option: ");
            if option <= 3 {
                break;
            }else {
                writeln_red!(term, "Please enter a valid option.");
            }
        }

        match option {
            1 => {

                //Get access token / API key
                match travis_ci::setup_api(&mut term, &mut config_file_path, &mut config) {
                    Ok(_)    => (),
                    Err(err) => {
                        writeln_red!(term, "Error setting up the travis-CI API token: {}", err);
                    }
                }

                //Save access token.
                if config_file_path != PathBuf::new() {
                    match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
                        Ok(_)    => (),
                        Err(err) => {
                            writeln_red!(term, "Error: {}", err);
                            writeln_red!(term, "Configuration file won't be used...");
                            config_file_path = PathBuf::new();
                        }
                    }
                }

                match travis_ci::create_travis_yml(&mut term, &config, &mut board_info, &output_direcrory) {
                    Ok(())   => (),
                    Err(err) => {
                        writeln_red!(term, "Error {}", err);
                    }
                }
            },
            2 => {

                //Get access token / API key
                appveyor::setup_api(&mut term, &mut config);

                //Save access token.
                if config_file_path != PathBuf::new() {
                    match config::TrelloBSTAPIConfig::save_config(&config_file_path, &config) {
                        Ok(_)    => (),
                        Err(err) => {
                            writeln_red!(term, "Error: {}", err);
                            writeln_red!(term, "Configuration file won't be used...");
                            config_file_path = PathBuf::new();
                        }
                    }
                }

                //Create appveyor.yml
                match appveyor::create_appveyor_yml(&mut term, &config, &mut board_info, &output_direcrory) {
                    Ok(()) => (),
                    Err(err) => {writeln_red!(term, "Error {}", err);}
                }
            },
            0 => exit(0),
            _ => {panic!("An invalid option slipped through...");}
        }
    }
}
<|endoftext|>