<commit_before>use std::ops::{Index, IndexMut};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Y(pub i32);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct X(pub i32);

pub type Coord = (Y, X);

#[derive(Debug, Clone)]
pub struct Grid<T: Clone> {
    height: i32,
    width: i32,
    data: Vec<T>,
}
impl<T: Clone> Grid<T> {
    pub fn new(height: i32, width: i32, default: T) -> Grid<T> {
        Grid {
            height: height,
            width: width,
            data: vec![default; (height * width) as usize],
        }
    }
    pub fn height(&self) -> i32 {
        self.height
    }
    pub fn width(&self) -> i32 {
        self.width
    }
    pub fn is_valid_coord(&self, (Y(y), X(x)): Coord) -> bool {
        0 <= y && y < self.height && 0 <= x && x < self.width
    }
    pub fn copy_from(&mut self, src: &Grid<T>) where T: Copy {
        assert_eq!(self.height, src.height);
        assert_eq!(self.width, src.width);
        self.data.copy_from_slice(&src.data);
    }
    pub fn index(&self, (Y(y), X(x)): Coord) -> usize {
        (y * self.width + x) as usize
    }
    pub fn coord(&self, idx: usize) -> Coord {
        let idx = idx as i32;
        (Y(idx / self.width), X(idx % self.width))
    }
}
impl<T: Clone> Index<Coord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: Coord) -> &'a T {
        let idx = self.index(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<Coord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: Coord) -> &'a mut T {
        let idx = self.index(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<usize> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: usize) -> &'a T {
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<usize> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut T {
        &mut self.data[idx]
    }
}

#[derive(Clone)]
pub struct FiniteSearchQueue {
    top: usize,
    end: usize,
    size: usize,
    queue: Vec<usize>,
    stored: Vec<bool>,
    is_started: bool,
}
impl FiniteSearchQueue {
    pub fn new(max_elem: usize) -> FiniteSearchQueue {
        FiniteSearchQueue {
            top: 0,
            end: 0,
            size: max_elem + 1,
            queue: vec![0; max_elem + 1],
            stored: vec![false; max_elem],
            is_started: false,
        }
    }
    pub fn is_started(&self) -> bool {
        self.is_started
    }
    pub fn start(&mut self) {
        self.is_started = true;
    }
    pub fn finish(&mut self) {
        self.is_started = false;
    }
    pub fn push(&mut self, v: usize) {
        if !self.stored[v] {
            self.stored[v] = true;
            let loc = self.end;
            self.end += 1;
            if self.end == self.size {
                self.end = 0;
            }
            self.queue[loc] = v;
        }
    }
    pub fn pop(&mut self) -> usize {
        let ret = self.queue[self.top];
        self.top += 1;
        if self.top == self.size {
            self.top = 0;
        }
        self.stored[ret] = false;
        ret
    }
    pub fn empty(&mut self) -> bool {
        self.top == self.end
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Symmetry {
    pub dyad: bool,         // 180-degree symmetry
    pub tetrad: bool,       // 90-degree symmetry
    pub horizontal: bool,   // horizontal line symmetry
    pub vertical: bool,     // vertical line symmetry
}

#[cfg(test)]
pub fn vec_to_grid<T>(v: &Vec<Vec<T>>) -> Grid<T>
where
    T: Copy,
{
    if v.len() == 0 {
        panic!("Attempted to convert empty Vec to Grid");
    }
    let ref_len = v[0].len();
    for r in v {
        if r.len() != ref_len {
            panic!("Each element in v must contain the same number of elements");
        }
    }
    let mut flat = vec![];
    for r in v {
        for &x in r {
            flat.push(x);
        }
    }
    Grid {
        height: v.len() as i32,
        width: ref_len as i32,
        data: flat,
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_grid() {
        let mut grid = Grid::new(3, 3, 0);
        assert_eq!(grid.height(), 3);
        assert_eq!(grid.width(), 3);
        assert_eq!(grid[(Y(1), X(1))], 0);
        grid[(Y(1), X(1))] = 4;
        assert_eq!(grid[(Y(1), X(1))], 4);
        assert_eq!(grid[(Y(1), X(0))], 0);
        assert_eq!(grid[(Y(2), X(1))], 0);
        assert_eq!(grid[4], 4);
    }
}
<commit_msg>Add Symmetry to common<commit_after>use std::ops::{Index, IndexMut};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Y(pub i32);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct X(pub i32);

pub type Coord = (Y, X);

#[derive(Debug, Clone)]
pub struct Grid<T: Clone> {
    height: i32,
    width: i32,
    data: Vec<T>,
}
impl<T: Clone> Grid<T> {
    pub fn new(height: i32, width: i32, default: T) -> Grid<T> {
        Grid {
            height: height,
            width: width,
            data: vec![default; (height * width) as usize],
        }
    }
    pub fn height(&self) -> i32 {
        self.height
    }
    pub fn width(&self) -> i32 {
        self.width
    }
    pub fn is_valid_coord(&self, (Y(y), X(x)): Coord) -> bool {
        0 <= y && y < self.height && 0 <= x && x < self.width
    }
    pub fn copy_from(&mut self, src: &Grid<T>) where T: Copy {
        assert_eq!(self.height, src.height);
        assert_eq!(self.width, src.width);
        self.data.copy_from_slice(&src.data);
    }
    pub fn index(&self, (Y(y), X(x)): Coord) -> usize {
        (y * self.width + x) as usize
    }
    pub fn coord(&self, idx: usize) -> Coord {
        let idx = idx as i32;
        (Y(idx / self.width), X(idx % self.width))
    }
}
impl<T: Clone> Index<Coord> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: Coord) -> &'a T {
        let idx = self.index(idx);
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<Coord> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: Coord) -> &'a mut T {
        let idx = self.index(idx);
        &mut self.data[idx]
    }
}
impl<T: Clone> Index<usize> for Grid<T> {
    type Output = T;
    fn index<'a>(&'a self, idx: usize) -> &'a T {
        &self.data[idx]
    }
}
impl<T: Clone> IndexMut<usize> for Grid<T> {
    fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut T {
        &mut self.data[idx]
    }
}

#[derive(Clone)]
pub struct FiniteSearchQueue {
    top: usize,
    end: usize,
    size: usize,
    queue: Vec<usize>,
    stored: Vec<bool>,
    is_started: bool,
}
impl FiniteSearchQueue {
    pub fn new(max_elem: usize) -> FiniteSearchQueue {
        FiniteSearchQueue {
            top: 0,
            end: 0,
            size: max_elem + 1,
            queue: vec![0; max_elem + 1],
            stored: vec![false; max_elem],
            is_started: false,
        }
    }
    pub fn is_started(&self) -> bool {
        self.is_started
    }
    pub fn start(&mut self) {
        self.is_started = true;
    }
    pub fn finish(&mut self) {
        self.is_started = false;
    }
    pub fn push(&mut self, v: usize) {
        if !self.stored[v] {
            self.stored[v] = true;
            let loc = self.end;
            self.end += 1;
            if self.end == self.size {
                self.end = 0;
            }
            self.queue[loc] = v;
        }
    }
    pub fn pop(&mut self) -> usize {
        let ret = self.queue[self.top];
        self.top += 1;
        if self.top == self.size {
            self.top = 0;
        }
        self.stored[ret] = false;
        ret
    }
    pub fn empty(&mut self) -> bool {
        self.top == self.end
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Symmetry {
    pub dyad: bool,         // 180-degree symmetry
    pub tetrad: bool,       // 90-degree symmetry
    pub horizontal: bool,   // horizontal line symmetry
    pub vertical: bool,     // vertical line symmetry
}

impl Symmetry {
    pub fn none() -> Symmetry {
        Symmetry {
            dyad: false,
            tetrad: false,
            horizontal: false,
            vertical: false,
        }
    }
}

#[cfg(test)]
pub fn vec_to_grid<T>(v: &Vec<Vec<T>>) -> Grid<T>
where
    T: Copy,
{
    if v.len() == 0 {
        panic!("Attempted to convert empty Vec to Grid");
    }
    let ref_len = v[0].len();
    for r in v {
        if r.len() != ref_len {
            panic!("Each element in v must contain the same number of elements");
        }
    }
    let mut flat = vec![];
    for r in v {
        for &x in r {
            flat.push(x);
        }
    }
    Grid {
        height: v.len() as i32,
        width: ref_len as i32,
        data: flat,
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_grid() {
        let mut grid = Grid::new(3, 3, 0);
        assert_eq!(grid.height(), 3);
        assert_eq!(grid.width(), 3);
        assert_eq!(grid[(Y(1), X(1))], 0);
        grid[(Y(1), X(1))] = 4;
        assert_eq!(grid[(Y(1), X(1))], 4);
        assert_eq!(grid[(Y(1), X(0))], 0);
        assert_eq!(grid[(Y(2), X(1))], 0);
        assert_eq!(grid[4], 4);
    }
}
<|endoftext|>


<commit_before>use core::clone::Clone;
use core::iter::Iterator;
use core::mem::size_of;
use core::ops::Drop;
use core::option::Option;
use core::ptr;
use core::slice;
use core::slice::SliceExt;

use syscall::call::*;

/// An iterator over a vec
pub struct VecIterator<'a, T: 'a> {
    vec: &'a Vec<T>,
    offset: usize,
}

impl <'a, T> Iterator for VecIterator<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item)
            }
            Option::None => {
                Option::None
            }
        }
    }
}

/// A owned, heap allocated list of elements
pub struct Vec<T> {
    pub data: *mut T,
    pub length: usize,
}

impl <T> Vec<T> {
    /// Create a empty vector
    pub fn new() -> Vec<T> {
        Vec::<T> {
            data: 0 as *mut T,
            length: 0,
        }
    }

    /// Convert to pointer
    pub unsafe fn as_ptr(&self) -> *const T {
        self.data
    }

    /// Convert from a raw (unsafe) buffer
    pub unsafe fn from_raw_buf(ptr: *const T, len: usize) -> Vec<T> {
        let data = sys_alloc(size_of::<T>() * len);

        ptr::copy(ptr, data as *mut T, len);

        Vec::<T> {
            data: data as *mut T,
            length: len,
        }
    }

    /// Get the nth element. Returns None if out of bounds.
    pub fn get(&self, i: usize) -> Option<&mut T> {
        if i >= self.length {
            Option::None
        } else {
            unsafe { Option::Some(&mut *self.data.offset(i as isize)) }
        }
    }

    /// Set the nth element
    pub fn set(&self, i: usize, value: T) {
        if i <= self.length {
            unsafe {
                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Insert element at a given position
    pub fn insert(&mut self, i: usize, value: T) {
        if i <= self.length {
            self.length += 1;
            unsafe {
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                //Move all things ahead of insert forward one
                let mut j = self.length - 1;
                while j > i {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize - 1)));
                    j -= 1;
                }

                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Remove a element and return it as a Option
    pub fn remove(&mut self, i: usize) -> Option<T> {
        if i < self.length {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(i as isize));

                //Move all things ahead of remove back one
                let mut j = i;
                while j < self.length {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize + 1)));
                    j += 1;
                }

                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Push an element to a vector
    pub fn push(&mut self, value: T) {
        self.length += 1;
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;
            ptr::write(self.data.offset(self.length as isize - 1), value);
        }
    }

    /// Pop the last element
    pub fn pop(&mut self) -> Option<T> {
        if self.length > 0 {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(self.length as isize));
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Get the length of the vector
    pub fn len(&self) -> usize {
        self.length
    }

    /// Create an iterator
    pub fn iter(&self) -> VecIterator<T> {
        VecIterator {
            vec: self,
            offset: 0,
        }
    }

    // TODO: Consider returning a slice instead
    pub fn sub(&self, start: usize, count: usize) -> Vec<T> {
        let mut i = start;
        if i > self.len() {
            i = self.len();
        }

        let mut j = i + count;
        if j > self.len() {
            j = self.len();
        }

        let length = j - i;
        if length == 0 {
            return Vec::new();
        }

        unsafe {
            let data = sys_alloc(length * size_of::<T>()) as *mut T;

            for k in i..j {
                ptr::write(data.offset((k - i) as isize),
                           ptr::read(self.data.offset(k as isize)));
            }

            Vec {
                data: data,
                length: length,
            }
        }
    }

    pub fn as_slice(&self) -> &[T] {
        if self.data as usize > 0 && self.length > 0 {
            unsafe { slice::from_raw_parts(self.data, self.length) }
        } else {
            &[]
        }
    }
}

impl<T> Vec<T> where T: Clone {
    /// Append a vector to another vector
    pub fn push_all(&mut self, vec: &Vec<T>) {
        let mut i = self.length as isize;
        self.length += vec.len();
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

            for value in vec.iter() {
                ptr::write(self.data.offset(i), value.clone());
                i += 1;
            }
        }
    }
}

impl<T> Clone for Vec<T> where T: Clone {
    fn clone(&self) -> Vec<T> {
        let mut ret = Vec::new();
        ret.push_all(self);
        ret
    }
}

impl<T> Drop for Vec<T> {
    fn drop(&mut self) {
        unsafe {
            for i in 0..self.len() {
                ptr::read(self.data.offset(i as isize));
            }

            sys_unalloc(self.data as usize);
            self.data = 0 as *mut T;
            self.length = 0;
        }
    }
}
<commit_msg>Add vec!<commit_after>use core::clone::Clone;
use core::iter::Iterator;
use core::mem::size_of;
use core::ops::Drop;
use core::option::Option;
use core::ptr;
use core::slice;
use core::slice::SliceExt;

use syscall::call::*;

#[macro_export]
macro_rules! vec {
    ($($x:expr),*) => (
        Vec::from_slice(&[$($x),*])
    );
    ($($x:expr,)*) => (vec![$($x),*])
}

/// An iterator over a vec
pub struct VecIterator<'a, T: 'a> {
    vec: &'a Vec<T>,
    offset: usize,
}

impl <'a, T> Iterator for VecIterator<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.vec.get(self.offset) {
            Option::Some(item) => {
                self.offset += 1;
                Option::Some(item)
            }
            Option::None => {
                Option::None
            }
        }
    }
}

/// A owned, heap allocated list of elements
pub struct Vec<T> {
    pub data: *mut T,
    pub length: usize,
}

impl <T> Vec<T> {
    /// Create a empty vector
    pub fn new() -> Vec<T> {
        Vec::<T> {
            data: 0 as *mut T,
            length: 0,
        }
    }

    /// Convert to pointer
    pub unsafe fn as_ptr(&self) -> *const T {
        self.data
    }

    /// Convert from a raw (unsafe) buffer
    pub unsafe fn from_raw_buf(ptr: *const T, len: usize) -> Vec<T> {
        let data = sys_alloc(size_of::<T>() * len) as *mut T;

        ptr::copy(ptr, data, len);

        Vec::<T> {
            data: data,
            length: len,
        }
    }

    pub fn from_slice(slice: &[T]) -> Vec<T> {
        let data;
        unsafe{
            data = sys_alloc(size_of::<T>() * slice.len()) as *mut T;

            ptr::copy(slice.as_ptr(), data, slice.len());
        }

        Vec::<T> {
            data: data,
            length: slice.len()
        }
    }

    /// Get the nth element. Returns None if out of bounds.
    pub fn get(&self, i: usize) -> Option<&mut T> {
        if i >= self.length {
            Option::None
        } else {
            unsafe { Option::Some(&mut *self.data.offset(i as isize)) }
        }
    }

    /// Set the nth element
    pub fn set(&self, i: usize, value: T) {
        if i <= self.length {
            unsafe {
                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Insert element at a given position
    pub fn insert(&mut self, i: usize, value: T) {
        if i <= self.length {
            self.length += 1;
            unsafe {
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                //Move all things ahead of insert forward one
                let mut j = self.length - 1;
                while j > i {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize - 1)));
                    j -= 1;
                }

                ptr::write(self.data.offset(i as isize), value);
            }
        }
    }

    /// Remove a element and return it as a Option
    pub fn remove(&mut self, i: usize) -> Option<T> {
        if i < self.length {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(i as isize));

                //Move all things ahead of remove back one
                let mut j = i;
                while j < self.length {
                    ptr::write(self.data.offset(j as isize),
                               ptr::read(self.data.offset(j as isize + 1)));
                    j += 1;
                }

                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Push an element to a vector
    pub fn push(&mut self, value: T) {
        self.length += 1;
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;
            ptr::write(self.data.offset(self.length as isize - 1), value);
        }
    }

    /// Pop the last element
    pub fn pop(&mut self) -> Option<T> {
        if self.length > 0 {
            self.length -= 1;
            unsafe {
                let item = ptr::read(self.data.offset(self.length as isize));
                self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

                Option::Some(item)
            }
        } else {
            Option::None
        }
    }

    /// Get the length of the vector
    pub fn len(&self) -> usize {
        self.length
    }

    /// Create an iterator
    pub fn iter(&self) -> VecIterator<T> {
        VecIterator {
            vec: self,
            offset: 0,
        }
    }

    // TODO: Consider returning a slice instead
    pub fn sub(&self, start: usize, count: usize) -> Vec<T> {
        let mut i = start;
        if i > self.len() {
            i = self.len();
        }

        let mut j = i + count;
        if j > self.len() {
            j = self.len();
        }

        let length = j - i;
        if length == 0 {
            return Vec::new();
        }

        unsafe {
            let data = sys_alloc(length * size_of::<T>()) as *mut T;

            for k in i..j {
                ptr::write(data.offset((k - i) as isize),
                           ptr::read(self.data.offset(k as isize)));
            }

            Vec {
                data: data,
                length: length,
            }
        }
    }

    pub fn as_slice(&self) -> &[T] {
        if self.data as usize > 0 && self.length > 0 {
            unsafe { slice::from_raw_parts(self.data, self.length) }
        } else {
            &[]
        }
    }
}

impl<T> Vec<T> where T: Clone {
    /// Append a vector to another vector
    pub fn push_all(&mut self, vec: &Vec<T>) {
        let mut i = self.length as isize;
        self.length += vec.len();
        unsafe {
            self.data = sys_realloc(self.data as usize, self.length * size_of::<T>()) as *mut T;

            for value in vec.iter() {
                ptr::write(self.data.offset(i), value.clone());
                i += 1;
            }
        }
    }
}

impl<T> Clone for Vec<T> where T: Clone {
    fn clone(&self) -> Vec<T> {
        let mut ret = Vec::new();
        ret.push_all(self);
        ret
    }
}

impl<T> Drop for Vec<T> {
    fn drop(&mut self) {
        unsafe {
            for i in 0..self.len() {
                ptr::read(self.data.offset(i as isize));
            }

            sys_unalloc(self.data as usize);
            self.data = 0 as *mut T;
            self.length = 0;
        }
    }
}
<|endoftext|>


<commit_before>//! Completion API
use std::collections::BTreeSet;
use std::fs;
use std::path::{self, Path};

use super::Result;
use line_buffer::LineBuffer;

// TODO: let the implementers choose/find word boudaries ???
// (line, pos) is like (rl_line_buffer, rl_point) to make contextual completion ("select t.na| from tbl as t")
// TOOD: make &self &mut self ???

/// To be called for tab-completion.
pub trait Completer {
    /// Takes the currently edited `line` with the cursor `pos`ition and
    /// returns the start position and the completion candidates for the partial word to be completed.
    /// "ls /usr/loc" => Ok((3, vec!["/usr/local/"]))
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)>;
    /// Updates the edited `line` with the `elected` candidate.
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        let end = line.pos();
        line.replace(start, end, elected)
    }
}

impl Completer for () {
    fn complete(&self, _line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
        Ok((0, Vec::new()))
    }
    fn update(&self, _line: &mut LineBuffer, _start: usize, _elected: &str) {
        unreachable!()
    }
}

impl<'c, C: ?Sized + Completer> Completer for &'c C {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        (**self).complete(line, pos)
    }
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        (**self).update(line, start, elected)
    }
}
macro_rules! box_completer {
    ($($id: ident)*) => {
        $(
            impl<C: ?Sized + Completer> Completer for $id<C> {
                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
                    (**self).complete(line, pos)
                }
                fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
                    (**self).update(line, start, elected)
                }
            }
        )*
    }
}

use std::rc::Rc;
use std::sync::Arc;
box_completer! { Box Rc Arc }

pub struct FilenameCompleter {
    break_chars: BTreeSet<char>,
}

#[cfg(unix)]
static DEFAULT_BREAK_CHARS: [char; 18] = [' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>',
                                          '<', '=', ';', '|', '&', '{', '(', '\0'];
// Remove \ to make file completion works on windows
#[cfg(windows)]
static DEFAULT_BREAK_CHARS: [char; 17] = [' ', '\t', '\n', '"', '\'', '`', '@', '$', '>', '<',
                                          '=', ';', '|', '&', '{', '(', '\0'];

impl FilenameCompleter {
    pub fn new() -> FilenameCompleter {
        FilenameCompleter { break_chars: DEFAULT_BREAK_CHARS.iter().cloned().collect() }
    }
}

impl Default for FilenameCompleter {
    fn default() -> FilenameCompleter {
        FilenameCompleter::new()
    }
}

impl Completer for FilenameCompleter {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        let (start, path) = extract_word(line, pos, &self.break_chars);
        let matches = try!(filename_complete(path));
        Ok((start, matches))
    }
}

fn filename_complete(path: &str) -> Result<Vec<String>> {
    use std::env::{current_dir, home_dir};

    let sep = path::MAIN_SEPARATOR;
    let (dir_name, file_name) = match path.rfind(sep) {
        Some(idx) => path.split_at(idx + sep.len_utf8()),
        None => ("", path),
    };

    let dir_path = Path::new(dir_name);
    let dir = if dir_path.starts_with("~") {
        // ~[/...]
        if let Some(home) = home_dir() {
            match dir_path.strip_prefix("~") {
                Ok(rel_path) => home.join(rel_path),
                _ => home,
            }
        } else {
            dir_path.to_path_buf()
        }
    } else if dir_path.is_relative() {
        // TODO ~user[/...] (https://crates.io/crates/users)
        if let Ok(cwd) = current_dir() {
            cwd.join(dir_path)
        } else {
            dir_path.to_path_buf()
        }
    } else {
        dir_path.to_path_buf()
    };

    let mut entries: Vec<String> = Vec::new();
    for entry in try!(dir.read_dir()) {
        let entry = try!(entry);
        if let Some(s) = entry.file_name().to_str() {
            if s.starts_with(file_name) {
                let mut path = String::from(dir_name) + s;
                if try!(fs::metadata(entry.path())).is_dir() {
                    path.push(sep);
                }
                entries.push(path);
            }
        }
    }
    Ok(entries)
}

/// Given a `line` and a cursor `pos`ition,
/// try to find backward the start of a word.
/// Return (0, `line[..pos]`) if no break char has been found.
/// Return the word and its start position (idx, `line[idx..pos]`) otherwise.
pub fn extract_word<'l>(line: &'l str,
                        pos: usize,
                        break_chars: &BTreeSet<char>)
                        -> (usize, &'l str) {
    let line = &line[..pos];
    if line.is_empty() {
        return (0, line);
    }
    match line.char_indices().rev().find(|&(_, c)| break_chars.contains(&c)) {
        Some((i, c)) => {
            let start = i + c.len_utf8();
            (start, &line[start..])
        }
        None => (0, line),
    }
}

pub fn longest_common_prefix(candidates: &[String]) -> Option<&str> {
    if candidates.is_empty() {
        return None;
    } else if candidates.len() == 1 {
        return Some(&candidates[0]);
    }
    let mut longest_common_prefix = 0;
    'o: loop {
        for i in 0..candidates.len() - 1 {
            let b1 = candidates[i].as_bytes();
            let b2 = candidates[i + 1].as_bytes();
            if b1.len() <= longest_common_prefix || b2.len() <= longest_common_prefix ||
               b1[longest_common_prefix] != b2[longest_common_prefix] {
                break 'o;
            }
        }
        longest_common_prefix += 1;
    }
    while !candidates[0].is_char_boundary(longest_common_prefix) {
        longest_common_prefix -= 1;
    }
    if longest_common_prefix == 0 {
        return None;
    }
    Some(&candidates[0][0..longest_common_prefix])
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;

    #[test]
    pub fn extract_word() {
        let break_chars: BTreeSet<char> = super::DEFAULT_BREAK_CHARS.iter().cloned().collect();
        let line = "ls '/usr/local/b";
        assert_eq!((4, "/usr/local/b"),
                   super::extract_word(line, line.len(), &break_chars));
    }

    #[test]
    pub fn longest_common_prefix() {
        let mut candidates = vec![];
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert!(lcp.is_none());
        }

        let s = "User";
        let c1 = String::from(s);
        candidates.push(c1.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert_eq!(Some(s), lcp);
        }

        let c2 = String::from("Users");
        candidates.push(c2.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert_eq!(Some(s), lcp);
        }

        let c3 = String::from("");
        candidates.push(c3.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert!(lcp.is_none());
        }

        let candidates = vec![String::from("fée"), String::from("fête")];
        let lcp = super::longest_common_prefix(&candidates);
        assert_eq!(Some("f"), lcp);
    }
}
<commit_msg>Fix file completion<commit_after>//! Completion API
use std::borrow::Cow::{self, Borrowed, Owned};
use std::collections::BTreeSet;
use std::fs;
use std::path::{self, Path};

use super::Result;
use line_buffer::LineBuffer;

// TODO: let the implementers choose/find word boudaries ???
// (line, pos) is like (rl_line_buffer, rl_point) to make contextual completion ("select t.na| from tbl as t")
// TOOD: make &self &mut self ???

/// To be called for tab-completion.
pub trait Completer {
    /// Takes the currently edited `line` with the cursor `pos`ition and
    /// returns the start position and the completion candidates for the partial word to be completed.
    /// "ls /usr/loc" => Ok((3, vec!["/usr/local/"]))
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)>;
    /// Updates the edited `line` with the `elected` candidate.
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        let end = line.pos();
        line.replace(start, end, elected)
    }
}

impl Completer for () {
    fn complete(&self, _line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
        Ok((0, Vec::new()))
    }
    fn update(&self, _line: &mut LineBuffer, _start: usize, _elected: &str) {
        unreachable!()
    }
}

impl<'c, C: ?Sized + Completer> Completer for &'c C {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        (**self).complete(line, pos)
    }
    fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
        (**self).update(line, start, elected)
    }
}
macro_rules! box_completer {
    ($($id: ident)*) => {
        $(
            impl<C: ?Sized + Completer> Completer for $id<C> {
                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
                    (**self).complete(line, pos)
                }
                fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
                    (**self).update(line, start, elected)
                }
            }
        )*
    }
}

use std::rc::Rc;
use std::sync::Arc;
box_completer! { Box Rc Arc }

pub struct FilenameCompleter {
    break_chars: BTreeSet<char>,
}

#[cfg(unix)]
static DEFAULT_BREAK_CHARS: [char; 18] = [' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>',
                                          '<', '=', ';', '|', '&', '{', '(', '\0'];
#[cfg(unix)]
static ESCAPE_CHAR: Option<char> = Some('\\');
// Remove \ to make file completion works on windows
#[cfg(windows)]
static DEFAULT_BREAK_CHARS: [char; 17] = [' ', '\t', '\n', '"', '\'', '`', '@', '$', '>', '<',
                                          '=', ';', '|', '&', '{', '(', '\0'];
#[cfg(windows)]
static ESCAPE_CHAR: Option<char> = None;

impl FilenameCompleter {
    pub fn new() -> FilenameCompleter {
        FilenameCompleter { break_chars: DEFAULT_BREAK_CHARS.iter().cloned().collect() }
    }
}

impl Default for FilenameCompleter {
    fn default() -> FilenameCompleter {
        FilenameCompleter::new()
    }
}

impl Completer for FilenameCompleter {
    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
        let (start, path) = extract_word(line, pos, ESCAPE_CHAR, &self.break_chars);
        let path = unescape(path, ESCAPE_CHAR);
        let matches = try!(filename_complete(&path, ESCAPE_CHAR, &self.break_chars));
        Ok((start, matches))
    }
}

/// Remove escape char
pub fn unescape(input: &str, esc_char: Option<char>) -> Cow<str> {
    if esc_char.is_none() {
        return Borrowed(input);
    }
    let esc_char = esc_char.unwrap();
    let n = input.chars().filter(|&c| c == esc_char).count();
    if n == 0 {
        return Borrowed(input);
    }
    let mut result = String::with_capacity(input.len() - n);
    let mut chars = input.chars();
    while let Some(ch) = chars.next() {
        if ch == esc_char {
            if let Some(ch) = chars.next() {
                result.push(ch);
            }
        } else {
            result.push(ch);
        }
    }
    Owned(result)
}

pub fn escape(input: String, esc_char: Option<char>, break_chars: &BTreeSet<char>) -> String {
    if esc_char.is_none() {
        return input;
    }
    let esc_char = esc_char.unwrap();
    let n = input.chars().filter(|c| break_chars.contains(c)).count();
    if n == 0 {
        return input;
    }
    let mut result = String::with_capacity(input.len() + n);

    for c in input.chars() {
        if break_chars.contains(&c) {
            result.push(esc_char);
        }
        result.push(c);
    }
    result
}

fn filename_complete(path: &str,
                     esc_char: Option<char>,
                     break_chars: &BTreeSet<char>)
                     -> Result<Vec<String>> {
    use std::env::{current_dir, home_dir};

    let sep = path::MAIN_SEPARATOR;
    let (dir_name, file_name) = match path.rfind(sep) {
        Some(idx) => path.split_at(idx + sep.len_utf8()),
        None => ("", path),
    };

    let dir_path = Path::new(dir_name);
    let dir = if dir_path.starts_with("~") {
        // ~[/...]
        if let Some(home) = home_dir() {
            match dir_path.strip_prefix("~") {
                Ok(rel_path) => home.join(rel_path),
                _ => home,
            }
        } else {
            dir_path.to_path_buf()
        }
    } else if dir_path.is_relative() {
        // TODO ~user[/...] (https://crates.io/crates/users)
        if let Ok(cwd) = current_dir() {
            cwd.join(dir_path)
        } else {
            dir_path.to_path_buf()
        }
    } else {
        dir_path.to_path_buf()
    };

    let mut entries: Vec<String> = Vec::new();
    for entry in try!(dir.read_dir()) {
        let entry = try!(entry);
        if let Some(s) = entry.file_name().to_str() {
            if s.starts_with(file_name) {
                let mut path = String::from(dir_name) + s;
                if try!(fs::metadata(entry.path())).is_dir() {
                    path.push(sep);
                }
                entries.push(escape(path, esc_char, break_chars));
            }
        }
    }
    Ok(entries)
}

/// Given a `line` and a cursor `pos`ition,
/// try to find backward the start of a word.
/// Return (0, `line[..pos]`) if no break char has been found.
/// Return the word and its start position (idx, `line[idx..pos]`) otherwise.
pub fn extract_word<'l>(line: &'l str,
                        pos: usize,
                        esc_char: Option<char>,
                        break_chars: &BTreeSet<char>)
                        -> (usize, &'l str) {
    let line = &line[..pos];
    if line.is_empty() {
        return (0, line);
    }
    let mut start = None;
    for (i, c) in line.char_indices().rev() {
        if esc_char.is_some() && start.is_some() {
            if esc_char.unwrap() == c {
                // escaped break char
                start = None;
                continue;
            } else {
                break;
            }
        }
        if break_chars.contains(&c) {
            start = Some(i + c.len_utf8());
            if esc_char.is_none() {
                break;
            } // else maybe escaped...
        }
    }

    match start {
        Some(start) => (start, &line[start..]),
        None => (0, line),
    }
}

pub fn longest_common_prefix(candidates: &[String]) -> Option<&str> {
    if candidates.is_empty() {
        return None;
    } else if candidates.len() == 1 {
        return Some(&candidates[0]);
    }
    let mut longest_common_prefix = 0;
    'o: loop {
        for i in 0..candidates.len() - 1 {
            let b1 = candidates[i].as_bytes();
            let b2 = candidates[i + 1].as_bytes();
            if b1.len() <= longest_common_prefix || b2.len() <= longest_common_prefix ||
               b1[longest_common_prefix] != b2[longest_common_prefix] {
                break 'o;
            }
        }
        longest_common_prefix += 1;
    }
    while !candidates[0].is_char_boundary(longest_common_prefix) {
        longest_common_prefix -= 1;
    }
    if longest_common_prefix == 0 {
        return None;
    }
    Some(&candidates[0][0..longest_common_prefix])
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;

    #[test]
    pub fn extract_word() {
        let break_chars: BTreeSet<char> = super::DEFAULT_BREAK_CHARS.iter().cloned().collect();
        let line = "ls '/usr/local/b";
        assert_eq!((4, "/usr/local/b"),
                   super::extract_word(line, line.len(), Some('\\'), &break_chars));
        let line = "ls /User\\ Information";
        assert_eq!((3, "/User\\ Information"),
                   super::extract_word(line, line.len(), Some('\\'), &break_chars));
    }

    #[test]
    pub fn unescape() {
        use std::borrow::Cow::{self, Borrowed, Owned};
        let input = "/usr/local/b";
        assert_eq!(Borrowed(input), super::unescape(input, Some('\\')));
        let input = "/User\\ Information";
        let result: Cow<str> = Owned(String::from("/User Information"));
        assert_eq!(result, super::unescape(input, Some('\\')));
    }

    #[test]
    pub fn escape() {
        let break_chars: BTreeSet<char> = super::DEFAULT_BREAK_CHARS.iter().cloned().collect();
        let input = String::from("/usr/local/b");
        assert_eq!(input.clone(),
                   super::escape(input, Some('\\'), &break_chars));
        let input = String::from("/User Information");
        let result = String::from("/User\\ Information");
        assert_eq!(result, super::escape(input, Some('\\'), &break_chars));
    }

    #[test]
    pub fn longest_common_prefix() {
        let mut candidates = vec![];
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert!(lcp.is_none());
        }

        let s = "User";
        let c1 = String::from(s);
        candidates.push(c1.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert_eq!(Some(s), lcp);
        }

        let c2 = String::from("Users");
        candidates.push(c2.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert_eq!(Some(s), lcp);
        }

        let c3 = String::from("");
        candidates.push(c3.clone());
        {
            let lcp = super::longest_common_prefix(&candidates);
            assert!(lcp.is_none());
        }

        let candidates = vec![String::from("fée"), String::from("fête")];
        let lcp = super::longest_common_prefix(&candidates);
        assert_eq!(Some("f"), lcp);
    }
}
<|endoftext|>


<commit_before>// Make-related variables are capitalized:
// This is not Rust-way, but makes code readable where we interface with Make
#![allow(non_snake_case)]

static COMPONENTS_VERSION: &'static str = "0.3.0";
static COMPONENTS_DATE:    &'static str = "2015-12-31";

use std::process;
use std::process::Command;
use std::process::Stdio;
use std::env;
use std::fs;
use std::fs::File;
use std::os::unix::prelude::*;

fn main() {
    let command: String;
    let explain_mode: bool;
    let debug_mode: bool = env::vars().any(|k| k.0 == "DEBUG" );

    if debug_mode {
        println!("Debug mode is enabled");
    }

    let mut args = env::args();

    if args.len() == 1 {
        println!("Components {} ({})\n\n{}", COMPONENTS_VERSION, COMPONENTS_DATE, COMPONENTS_USAGE_INSTRUCTIONS);

        process::exit(0);
    }

    args.next(); // skipping arg[0]

    let explain_or_command = args.next().unwrap();
    if explain_or_command == "explain" {
         command = args.next().unwrap_or_else(|| {
             println!("provide a command to explain: install, update, purge etc");
             process::exit(1);
         });

         explain_mode = true;
    } else {
         command = explain_or_command;
         explain_mode = false;
    }

    if debug_mode {
         println!("command is: {}", &command);
    }

    let available_commands = ["install", "purge", "clean", "uninstall", "update"];

    let index = available_commands.iter().position(|&r| r == command);
    if index.is_some() == false {
        println!("invalid command: {}", command);
        process::exit(1);
    }

    let current_dir = env::current_dir().unwrap();
    let home_dir    = env::home_dir().unwrap();

    if debug_mode {
        println!("The current directory is: {}", current_dir.display());
        println!("The home directory is: {}", home_dir.display());
    }

    // Local
    let COMPONENTS_TEMP_PATH: &'static str = "/tmp/Components";

    // Global (we'll pass them to Make)
    let COMPONENTS_BUILD_CACHE_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_BUILD_CACHE_PATH") {
	COMPONENTS_BUILD_CACHE_PATH = std::path::PathBuf::from(value);
    } else {
	COMPONENTS_BUILD_CACHE_PATH = home_dir.join("Library/Caches/Components");
    }

    let COMPONENTS_INSTALL_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_INSTALL_PATH") {
	COMPONENTS_INSTALL_PATH = std::path::PathBuf::from(value);
    } else {
	COMPONENTS_INSTALL_PATH = current_dir.join("Components");
    }

    let COMPONENTS_MAKE_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_MAKE_PATH") {
        COMPONENTS_MAKE_PATH  = std::path::PathBuf::from(value);
    } else {
        COMPONENTS_MAKE_PATH  = current_dir.join("Components.make");
    }

    if debug_mode {
        println!("COMPONENTS_MAKE_PATH: {}", COMPONENTS_MAKE_PATH.display());
        println!("COMPONENTS_INSTALL_PATH: {}", COMPONENTS_INSTALL_PATH.display());
        println!("COMPONENTS_BUILD_CACHE_PATH: {}", COMPONENTS_BUILD_CACHE_PATH.display());
    }

    if COMPONENTS_MAKE_PATH.exists() == false {
        println!("Directory COMPONENTS_MAKE_PATH does not exist: {:?}", COMPONENTS_MAKE_PATH);
        process::exit(1);
    }

    fs::create_dir_all(&COMPONENTS_BUILD_CACHE_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_BUILD_CACHE_PATH: {:?}", COMPONENTS_BUILD_CACHE_PATH);
        process::exit(1);
    });

    fs::create_dir_all(&COMPONENTS_INSTALL_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_INSTALL_PATH: {:?}", COMPONENTS_INSTALL_PATH);
        process::exit(1);
    });

    fs::create_dir_all(&COMPONENTS_TEMP_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_TEMP_PATH: {:?}", COMPONENTS_TEMP_PATH);
        process::exit(1);
    });

    let mut makeflags: Vec<&str> = vec!["--warn-undefined-variables"];

    if explain_mode {
        makeflags.push("-n");
    }

    if debug_mode {
        makeflags.push("-r");
        makeflags.push("-d");
        println!("Makeflags: {:?}", makeflags);
    }

    let components: Vec<String> = args.collect();

    let component_paths: Vec<std::path::PathBuf>;

    if components.len() > 0 {
        if debug_mode {
            println!("Components: {:?}", components);
        }

        let mut should_break = false;

        let existing_components = {
            let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

            existing_component_paths_iterator.map(|entry| {
                let entry = entry.unwrap();
                let entry_path = entry.path();
                entry_path.file_stem().unwrap().to_str().unwrap().to_string()
            }).collect::<Vec<String>>()
        };

        for component in &components {
            let component_exists = existing_components.iter().any(|existing_component| existing_component == component);

            if component_exists == false {
                println!("Could not find component: {}", component);
                should_break = true;
            }
        }

        if should_break {
            process::exit(1);
        }

        let selected_component_paths_iterator = {
            let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

            existing_component_paths_iterator.filter(|entry| {
                let entry = entry.as_ref().unwrap();
                let entry_path = entry.path();
                let component_name = entry_path.file_stem().unwrap().to_str().unwrap();
                components.iter().any(|c| c == component_name)
            })
        };

        component_paths = selected_component_paths_iterator.map(|entry| {
            let entry = entry.unwrap();
            let entry_path = entry.path();
            entry_path
        }).collect::<Vec<std::path::PathBuf>>();
    } else {
        let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

        component_paths = existing_component_paths_iterator.map(|entry| {
            let entry = entry.unwrap();
            let entry_path = entry.path();
            entry_path
        }).collect::<Vec<std::path::PathBuf>>();
    }

    let mut success = true;
    for path in component_paths {
        let path_is_valid_component_make = path.is_file() && path.extension().is_some() && path.extension().unwrap() == "make";

        if path_is_valid_component_make == false {
            println!("COMPONENTS_MAKE_PATH directory contains non-builable artefacts (must only consist of *.make)");
            process::exit(1);
        }

        unsafe {
            let component_name = path.file_stem().unwrap().to_str().unwrap();

            let logpath = format!("/tmp/Components/{}.log", component_name);

            let logfile = File::create(&logpath).unwrap();

            let fd = logfile.as_raw_fd();

            let (stdout, stderr) = if debug_mode || explain_mode {
                (Stdio::inherit(), Stdio::inherit())
            } else {
                (Stdio::from_raw_fd(fd), Stdio::from_raw_fd(fd))
            };

            print!("[{}] {}:", command, component_name);

            let status = Command::new("make")
                .arg(&command)
                .args(&makeflags)
                .arg("-f")
                .arg(path.to_str().unwrap())
                .env("COMPONENTS_INSTALL_PATH", COMPONENTS_INSTALL_PATH.as_os_str())
                .env("COMPONENTS_MAKE_PATH", COMPONENTS_MAKE_PATH.as_os_str())
                .stdout(stdout)
                .stderr(stderr)
                .status().unwrap_or_else(|e| {
                    panic!("failed to execute process: {}", e)
                });

            if status.success() {
                print!(" done.\n");
            } else {
                print!(" failed. See log for details:\n{}\n", &logpath);
                success = false;
            }
        }
    }

    if success {
        process::exit(0);
    } else {
        process::exit(1);
    }
}

static COMPONENTS_USAGE_INSTRUCTIONS: &'static str = "\
Usage:
  components [explain] command [components]

  command:
    install   - downloads, builds (if needed) and installs component
                into a vendor directory (COMPONENTS_INSTALL_PATH)
    uninstall - removes component from a vendor directory (COMPONENTS_INSTALL_PATH)
    clean     - removes downloaded files and built artefacts from
                cache directory (COMPONENTS_BUILD_CACHE_PATH)
    purge     - cleans and uninstalls component
    update    - uninstalls current and installs new version of a component

  explain:
    print the commands that would be executed, but do not execute them

  components:
    one or more component name, enumerates all components in the components
    directory (COMPONENTS_MAKE_PATH) if the parameter is omitted

Directories:
  COMPONENTS_MAKE_PATH         - directory contains all components used in the current project
                               $COMPONENTS_MAKE_PATH
  COMPONENTS_INSTALL_PATH      - directory contains all installed components for the current project
                               $COMPONENTS_INSTALL_PATH
  COMPONENTS_BUILD_CACHE_PATH  - stores zip/tarballs, built artefacts, or source code of used components
                               $COMPONENTS_BUILD_CACHE_PATH

Debugging:
  Set environment variable 'DEBUG' to 'YES' to enable debugging output.
  It will run make with additional parameters '-r -d' and will send output to 'STDOUT'.

  From 'man make':
    -d  Print debugging information in addition to normal processing.
    -r  Eliminate use of the built−in implicit rules.

Examples:
  components install                # installs every component from the components directory (COMPONENTS_MAKE_PATH)
  components purge Cedar            # cleans and uninstalls Cedar library
  components update BloodMagic      # uninstalls current and installs new version of BloodMagic library
  components clean BloodMagic Cedar # cleans up Cedar' and BloodMagic' artefacts
  components explain install        # prints commands that would be executed to install each component
  DEBUG=YES components install      # prints additional information to 'STDOUT'\
";

<commit_msg>Read version automatically from Cargo.toml file<commit_after>// Make-related variables are capitalized:
// This is not Rust-way, but makes code readable where we interface with Make
#![allow(non_snake_case)]

static COMPONENTS_VERSION: &'static str = env!("CARGO_PKG_VERSION");
static COMPONENTS_DATE:    &'static str = "2015-12-31";

use std::process;
use std::process::Command;
use std::process::Stdio;
use std::env;
use std::fs;
use std::fs::File;
use std::os::unix::prelude::*;

fn main() {
    let command: String;
    let explain_mode: bool;
    let debug_mode: bool = env::vars().any(|k| k.0 == "DEBUG" );

    if debug_mode {
        println!("Debug mode is enabled");
    }

    let mut args = env::args();

    if args.len() == 1 {
        println!("Components {} ({})\n\n{}", COMPONENTS_VERSION, COMPONENTS_DATE, COMPONENTS_USAGE_INSTRUCTIONS);

        process::exit(0);
    }

    args.next(); // skipping arg[0]

    let explain_or_command = args.next().unwrap();
    if explain_or_command == "explain" {
         command = args.next().unwrap_or_else(|| {
             println!("provide a command to explain: install, update, purge etc");
             process::exit(1);
         });

         explain_mode = true;
    } else {
         command = explain_or_command;
         explain_mode = false;
    }

    if debug_mode {
         println!("command is: {}", &command);
    }

    let available_commands = ["install", "purge", "clean", "uninstall", "update"];

    let index = available_commands.iter().position(|&r| r == command);
    if index.is_some() == false {
        println!("invalid command: {}", command);
        process::exit(1);
    }

    let current_dir = env::current_dir().unwrap();
    let home_dir    = env::home_dir().unwrap();

    if debug_mode {
        println!("The current directory is: {}", current_dir.display());
        println!("The home directory is: {}", home_dir.display());
    }

    // Local
    let COMPONENTS_TEMP_PATH: &'static str = "/tmp/Components";

    // Global (we'll pass them to Make)
    let COMPONENTS_BUILD_CACHE_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_BUILD_CACHE_PATH") {
	COMPONENTS_BUILD_CACHE_PATH = std::path::PathBuf::from(value);
    } else {
	COMPONENTS_BUILD_CACHE_PATH = home_dir.join("Library/Caches/Components");
    }

    let COMPONENTS_INSTALL_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_INSTALL_PATH") {
	COMPONENTS_INSTALL_PATH = std::path::PathBuf::from(value);
    } else {
	COMPONENTS_INSTALL_PATH = current_dir.join("Components");
    }

    let COMPONENTS_MAKE_PATH: std::path::PathBuf;
    if let Ok(value) = std::env::var("COMPONENTS_MAKE_PATH") {
        COMPONENTS_MAKE_PATH  = std::path::PathBuf::from(value);
    } else {
        COMPONENTS_MAKE_PATH  = current_dir.join("Components.make");
    }

    if debug_mode {
        println!("COMPONENTS_MAKE_PATH: {}", COMPONENTS_MAKE_PATH.display());
        println!("COMPONENTS_INSTALL_PATH: {}", COMPONENTS_INSTALL_PATH.display());
        println!("COMPONENTS_BUILD_CACHE_PATH: {}", COMPONENTS_BUILD_CACHE_PATH.display());
    }

    if COMPONENTS_MAKE_PATH.exists() == false {
        println!("Directory COMPONENTS_MAKE_PATH does not exist: {:?}", COMPONENTS_MAKE_PATH);
        process::exit(1);
    }

    fs::create_dir_all(&COMPONENTS_BUILD_CACHE_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_BUILD_CACHE_PATH: {:?}", COMPONENTS_BUILD_CACHE_PATH);
        process::exit(1);
    });

    fs::create_dir_all(&COMPONENTS_INSTALL_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_INSTALL_PATH: {:?}", COMPONENTS_INSTALL_PATH);
        process::exit(1);
    });

    fs::create_dir_all(&COMPONENTS_TEMP_PATH).unwrap_or_else(|_| {
        println!("failed to create dir COMPONENTS_TEMP_PATH: {:?}", COMPONENTS_TEMP_PATH);
        process::exit(1);
    });

    let mut makeflags: Vec<&str> = vec!["--warn-undefined-variables"];

    if explain_mode {
        makeflags.push("-n");
    }

    if debug_mode {
        makeflags.push("-r");
        makeflags.push("-d");
        println!("Makeflags: {:?}", makeflags);
    }

    let components: Vec<String> = args.collect();

    let component_paths: Vec<std::path::PathBuf>;

    if components.len() > 0 {
        if debug_mode {
            println!("Components: {:?}", components);
        }

        let mut should_break = false;

        let existing_components = {
            let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

            existing_component_paths_iterator.map(|entry| {
                let entry = entry.unwrap();
                let entry_path = entry.path();
                entry_path.file_stem().unwrap().to_str().unwrap().to_string()
            }).collect::<Vec<String>>()
        };

        for component in &components {
            let component_exists = existing_components.iter().any(|existing_component| existing_component == component);

            if component_exists == false {
                println!("Could not find component: {}", component);
                should_break = true;
            }
        }

        if should_break {
            process::exit(1);
        }

        let selected_component_paths_iterator = {
            let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

            existing_component_paths_iterator.filter(|entry| {
                let entry = entry.as_ref().unwrap();
                let entry_path = entry.path();
                let component_name = entry_path.file_stem().unwrap().to_str().unwrap();
                components.iter().any(|c| c == component_name)
            })
        };

        component_paths = selected_component_paths_iterator.map(|entry| {
            let entry = entry.unwrap();
            let entry_path = entry.path();
            entry_path
        }).collect::<Vec<std::path::PathBuf>>();
    } else {
        let existing_component_paths_iterator: fs::ReadDir = fs::read_dir(&COMPONENTS_MAKE_PATH).unwrap();

        component_paths = existing_component_paths_iterator.map(|entry| {
            let entry = entry.unwrap();
            let entry_path = entry.path();
            entry_path
        }).collect::<Vec<std::path::PathBuf>>();
    }

    let mut success = true;
    for path in component_paths {
        let path_is_valid_component_make = path.is_file() && path.extension().is_some() && path.extension().unwrap() == "make";

        if path_is_valid_component_make == false {
            println!("COMPONENTS_MAKE_PATH directory contains non-builable artefacts (must only consist of *.make)");
            process::exit(1);
        }

        unsafe {
            let component_name = path.file_stem().unwrap().to_str().unwrap();

            let logpath = format!("/tmp/Components/{}.log", component_name);

            let logfile = File::create(&logpath).unwrap();

            let fd = logfile.as_raw_fd();

            let (stdout, stderr) = if debug_mode || explain_mode {
                (Stdio::inherit(), Stdio::inherit())
            } else {
                (Stdio::from_raw_fd(fd), Stdio::from_raw_fd(fd))
            };

            print!("[{}] {}:", command, component_name);

            let status = Command::new("make")
                .arg(&command)
                .args(&makeflags)
                .arg("-f")
                .arg(path.to_str().unwrap())
                .env("COMPONENTS_INSTALL_PATH", COMPONENTS_INSTALL_PATH.as_os_str())
                .env("COMPONENTS_MAKE_PATH", COMPONENTS_MAKE_PATH.as_os_str())
                .stdout(stdout)
                .stderr(stderr)
                .status().unwrap_or_else(|e| {
                    panic!("failed to execute process: {}", e)
                });

            if status.success() {
                print!(" done.\n");
            } else {
                print!(" failed. See log for details:\n{}\n", &logpath);
                success = false;
            }
        }
    }

    if success {
        process::exit(0);
    } else {
        process::exit(1);
    }
}

static COMPONENTS_USAGE_INSTRUCTIONS: &'static str = "\
Usage:
  components [explain] command [components]

  command:
    install   - downloads, builds (if needed) and installs component
                into a vendor directory (COMPONENTS_INSTALL_PATH)
    uninstall - removes component from a vendor directory (COMPONENTS_INSTALL_PATH)
    clean     - removes downloaded files and built artefacts from
                cache directory (COMPONENTS_BUILD_CACHE_PATH)
    purge     - cleans and uninstalls component
    update    - uninstalls current and installs new version of a component

  explain:
    print the commands that would be executed, but do not execute them

  components:
    one or more component name, enumerates all components in the components
    directory (COMPONENTS_MAKE_PATH) if the parameter is omitted

Directories:
  COMPONENTS_MAKE_PATH         - directory contains all components used in the current project
                               $COMPONENTS_MAKE_PATH
  COMPONENTS_INSTALL_PATH      - directory contains all installed components for the current project
                               $COMPONENTS_INSTALL_PATH
  COMPONENTS_BUILD_CACHE_PATH  - stores zip/tarballs, built artefacts, or source code of used components
                               $COMPONENTS_BUILD_CACHE_PATH

Debugging:
  Set environment variable 'DEBUG' to 'YES' to enable debugging output.
  It will run make with additional parameters '-r -d' and will send output to 'STDOUT'.

  From 'man make':
    -d  Print debugging information in addition to normal processing.
    -r  Eliminate use of the built−in implicit rules.

Examples:
  components install                # installs every component from the components directory (COMPONENTS_MAKE_PATH)
  components purge Cedar            # cleans and uninstalls Cedar library
  components update BloodMagic      # uninstalls current and installs new version of BloodMagic library
  components clean BloodMagic Cedar # cleans up Cedar' and BloodMagic' artefacts
  components explain install        # prints commands that would be executed to install each component
  DEBUG=YES components install      # prints additional information to 'STDOUT'\
";

<|endoftext|>


<commit_before>use std::ops::{BitOr, BitXor};
use super::{chtype, A_ALTCHARSET, A_BOLD, A_BLINK, A_CHARTEXT, A_DIM, A_LEFTLINE, A_INVIS};
use super::{A_ITALIC, A_OVERLINE, A_REVERSE, A_RIGHTLINE, A_STRIKEOUT, A_UNDERLINE};
use super::colorpair::ColorPair;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Attribute {
    AlternativeCharSet,
    Bold,
    Blink,
    CharText,
    Dim,
    Leftline,
    Invisible,
    Italic,
    Normal,
    Overline,
    Reverse,
    Rightline,
    Strikeout,
    Underline,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Attributes {
    raw: chtype,
    color_pair: ColorPair,
}

macro_rules! attribute_setter {
    ($name:ident, $attr:ident) => {
        pub fn $name(&mut self, enabled: bool) {
            if enabled {
                self.raw = self.raw | $attr;
            } else {
                self.raw = self.raw ^ $attr;
            }
        }
    };
}

impl Attributes {
    pub fn new() -> Attributes {
        Attributes {
            raw: 0,
            color_pair: ColorPair(0),
        }
    }

    pub fn is_alternative_char_set(&self) -> bool {
        (self.raw & A_ALTCHARSET) > 0
    }
    attribute_setter!(set_alternative_char_set, A_ALTCHARSET);

    pub fn is_bold(&self) -> bool {
        (self.raw & A_BOLD) > 0
    }
    attribute_setter!(set_bold, A_BOLD);

    pub fn is_blink(&self) -> bool {
        (self.raw & A_BLINK) > 0
    }
    attribute_setter!(set_blink, A_BLINK);

    pub fn is_char_text(&self) -> bool {
        (self.raw & A_CHARTEXT) > 0
    }
    attribute_setter!(set_char_text, A_CHARTEXT);

    pub fn is_dim(&self) -> bool {
        (self.raw & A_DIM) > 0
    }
    attribute_setter!(set_dim, A_DIM);

    pub fn is_leftline(&self) -> bool {
        (self.raw & A_LEFTLINE) > 0
    }
    attribute_setter!(set_leftline, A_LEFTLINE);

    pub fn is_invisible(&self) -> bool {
        (self.raw & A_INVIS) > 0
    }
    attribute_setter!(set_invisible, A_INVIS);

    pub fn is_italic(&self) -> bool {
        (self.raw & A_ITALIC) > 0
    }
    attribute_setter!(set_italic, A_ITALIC);

    pub fn is_normal(&self) -> bool {
        self.raw == 0
    }
    pub fn set_normal(&mut self) {
        self.raw = 0
    }

    pub fn is_overline(&self) -> bool {
        (self.raw & A_OVERLINE) > 0
    }
    attribute_setter!(set_overline, A_OVERLINE);

    pub fn is_reverse(&self) -> bool {
        (self.raw & A_REVERSE) > 0
    }
    attribute_setter!(set_reverse, A_REVERSE);

    pub fn is_rightline(&self) -> bool {
        (self.raw & A_RIGHTLINE) > 0
    }
    attribute_setter!(set_rightline, A_RIGHTLINE);

    pub fn is_strikeout(&self) -> bool {
        (self.raw & A_STRIKEOUT) > 0
    }
    attribute_setter!(set_strikeout, A_STRIKEOUT);

    pub fn is_underline(&self) -> bool {
        (self.raw & A_UNDERLINE) > 0
    }
    attribute_setter!(set_underline, A_UNDERLINE);

    pub fn color_pair(&self) -> ColorPair {
        self.color_pair
    }
    pub fn set_color_pair(&mut self, color_pair: ColorPair) {
        let color_chtype: chtype = color_pair.into();
        self.raw = self.raw | color_chtype;
        self.color_pair = color_pair;
    }
}

/// Implement the | operator for adding an Attribute to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new();
/// assert!(!attributes.is_bold());
/// attributes = attributes | Attribute::Bold;
/// assert!(attributes.is_bold());
/// ```
impl BitOr<Attribute> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(true),
            Attribute::Bold => self.set_bold(true),
            Attribute::Blink => self.set_blink(true),
            Attribute::CharText => self.set_char_text(true),
            Attribute::Dim => self.set_dim(true),
            Attribute::Leftline => self.set_leftline(true),
            Attribute::Invisible => self.set_invisible(true),
            Attribute::Italic => self.set_italic(true),
            Attribute::Normal => self.set_normal(),
            Attribute::Overline => self.set_overline(true),
            Attribute::Reverse => self.set_reverse(true),
            Attribute::Rightline => self.set_rightline(true),
            Attribute::Strikeout => self.set_strikeout(true),
            Attribute::Underline => self.set_underline(true),
        }
        self
    }
}

/// Implement the ^ operator for disabling an Attribute from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::from(Attribute::Bold);
/// assert!(attributes.is_bold());
/// attributes = attributes ^ Attribute::Bold;
/// assert!(!attributes.is_bold());
/// ```
impl BitXor<Attribute> for Attributes {
    type Output = Attributes;

    fn bitxor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(false),
            Attribute::Bold => self.set_bold(false),
            Attribute::Blink => self.set_blink(false),
            Attribute::CharText => self.set_char_text(false),
            Attribute::Dim => self.set_dim(false),
            Attribute::Leftline => self.set_leftline(false),
            Attribute::Invisible => self.set_invisible(false),
            Attribute::Italic => self.set_italic(false),
            Attribute::Normal => (),
            Attribute::Overline => self.set_overline(false),
            Attribute::Reverse => self.set_reverse(false),
            Attribute::Rightline => self.set_rightline(false),
            Attribute::Strikeout => self.set_strikeout(false),
            Attribute::Underline => self.set_underline(false),
        }
        self
    }
}

/// Implement the | operator for adding Attributes to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse;
/// attributes = attributes | other;
/// assert!(attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(!attributes.is_italic());
/// ```
impl BitOr for Attributes {
    type Output = Attributes;

    fn bitor(self, rhs: Attributes) -> Attributes {
        Attributes {
            raw: self.raw | rhs.raw,
            color_pair: ColorPair(self.color_pair.0 | rhs.color_pair.0),
        }
    }
}

/// Implement the ^ operator for removing Attributes from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Blink | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse | Attribute::Bold;
/// attributes = attributes ^ other;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(attributes.is_blink());
/// ```
impl BitXor for Attributes {
    type Output = Attributes;

    fn bitxor(self, rhs: Attributes) -> Attributes {
        Attributes {
            raw: self.raw ^ rhs.raw,
            color_pair: ColorPair(self.color_pair.0 ^ rhs.color_pair.0),
        }
    }
}

/// Implement the | operator for combining two 'Attribute's into Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let attributes = Attribute::Blink | Attribute::Reverse;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// assert!(attributes.is_reverse());
/// ```
impl BitOr for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

impl From<Attribute> for Attributes {
    fn from(attribute: Attribute) -> Attributes {
        Attributes::new() | attribute
    }
}

impl From<Attribute> for chtype {
    fn from(attribute: Attribute) -> chtype {
        chtype::from(Attributes::from(attribute))
    }
}

impl From<Attributes> for chtype {
    fn from(attributes: Attributes) -> chtype {
        attributes.raw
    }
}
<commit_msg>Implemented Default for Attributes<commit_after>use std::ops::{BitOr, BitXor};
use super::{chtype, A_ALTCHARSET, A_BLINK, A_BOLD, A_CHARTEXT, A_DIM, A_INVIS, A_LEFTLINE};
use super::{A_ITALIC, A_OVERLINE, A_REVERSE, A_RIGHTLINE, A_STRIKEOUT, A_UNDERLINE};
use super::colorpair::ColorPair;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Attribute {
    AlternativeCharSet,
    Bold,
    Blink,
    CharText,
    Dim,
    Leftline,
    Invisible,
    Italic,
    Normal,
    Overline,
    Reverse,
    Rightline,
    Strikeout,
    Underline,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Attributes {
    raw: chtype,
    color_pair: ColorPair,
}

macro_rules! attribute_setter {
    ($name:ident, $attr:ident) => {
        pub fn $name(&mut self, enabled: bool) {
            if enabled {
                self.raw = self.raw | $attr;
            } else {
                self.raw = self.raw ^ $attr;
            }
        }
    };
}

impl Attributes {
    pub fn new() -> Attributes {
        Attributes {
            raw: 0,
            color_pair: ColorPair(0),
        }
    }

    pub fn is_alternative_char_set(&self) -> bool {
        (self.raw & A_ALTCHARSET) > 0
    }
    attribute_setter!(set_alternative_char_set, A_ALTCHARSET);

    pub fn is_bold(&self) -> bool {
        (self.raw & A_BOLD) > 0
    }
    attribute_setter!(set_bold, A_BOLD);

    pub fn is_blink(&self) -> bool {
        (self.raw & A_BLINK) > 0
    }
    attribute_setter!(set_blink, A_BLINK);

    pub fn is_char_text(&self) -> bool {
        (self.raw & A_CHARTEXT) > 0
    }
    attribute_setter!(set_char_text, A_CHARTEXT);

    pub fn is_dim(&self) -> bool {
        (self.raw & A_DIM) > 0
    }
    attribute_setter!(set_dim, A_DIM);

    pub fn is_leftline(&self) -> bool {
        (self.raw & A_LEFTLINE) > 0
    }
    attribute_setter!(set_leftline, A_LEFTLINE);

    pub fn is_invisible(&self) -> bool {
        (self.raw & A_INVIS) > 0
    }
    attribute_setter!(set_invisible, A_INVIS);

    pub fn is_italic(&self) -> bool {
        (self.raw & A_ITALIC) > 0
    }
    attribute_setter!(set_italic, A_ITALIC);

    pub fn is_normal(&self) -> bool {
        self.raw == 0
    }
    pub fn set_normal(&mut self) {
        self.raw = 0
    }

    pub fn is_overline(&self) -> bool {
        (self.raw & A_OVERLINE) > 0
    }
    attribute_setter!(set_overline, A_OVERLINE);

    pub fn is_reverse(&self) -> bool {
        (self.raw & A_REVERSE) > 0
    }
    attribute_setter!(set_reverse, A_REVERSE);

    pub fn is_rightline(&self) -> bool {
        (self.raw & A_RIGHTLINE) > 0
    }
    attribute_setter!(set_rightline, A_RIGHTLINE);

    pub fn is_strikeout(&self) -> bool {
        (self.raw & A_STRIKEOUT) > 0
    }
    attribute_setter!(set_strikeout, A_STRIKEOUT);

    pub fn is_underline(&self) -> bool {
        (self.raw & A_UNDERLINE) > 0
    }
    attribute_setter!(set_underline, A_UNDERLINE);

    pub fn color_pair(&self) -> ColorPair {
        self.color_pair
    }
    pub fn set_color_pair(&mut self, color_pair: ColorPair) {
        let color_chtype: chtype = color_pair.into();
        self.raw = self.raw | color_chtype;
        self.color_pair = color_pair;
    }
}

/// Implement the | operator for adding an Attribute to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new();
/// assert!(!attributes.is_bold());
/// attributes = attributes | Attribute::Bold;
/// assert!(attributes.is_bold());
/// ```
impl BitOr<Attribute> for Attributes {
    type Output = Attributes;

    fn bitor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(true),
            Attribute::Bold => self.set_bold(true),
            Attribute::Blink => self.set_blink(true),
            Attribute::CharText => self.set_char_text(true),
            Attribute::Dim => self.set_dim(true),
            Attribute::Leftline => self.set_leftline(true),
            Attribute::Invisible => self.set_invisible(true),
            Attribute::Italic => self.set_italic(true),
            Attribute::Normal => self.set_normal(),
            Attribute::Overline => self.set_overline(true),
            Attribute::Reverse => self.set_reverse(true),
            Attribute::Rightline => self.set_rightline(true),
            Attribute::Strikeout => self.set_strikeout(true),
            Attribute::Underline => self.set_underline(true),
        }
        self
    }
}

/// Implement the ^ operator for disabling an Attribute from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::from(Attribute::Bold);
/// assert!(attributes.is_bold());
/// attributes = attributes ^ Attribute::Bold;
/// assert!(!attributes.is_bold());
/// ```
impl BitXor<Attribute> for Attributes {
    type Output = Attributes;

    fn bitxor(mut self, rhs: Attribute) -> Attributes {
        match rhs {
            Attribute::AlternativeCharSet => self.set_alternative_char_set(false),
            Attribute::Bold => self.set_bold(false),
            Attribute::Blink => self.set_blink(false),
            Attribute::CharText => self.set_char_text(false),
            Attribute::Dim => self.set_dim(false),
            Attribute::Leftline => self.set_leftline(false),
            Attribute::Invisible => self.set_invisible(false),
            Attribute::Italic => self.set_italic(false),
            Attribute::Normal => (),
            Attribute::Overline => self.set_overline(false),
            Attribute::Reverse => self.set_reverse(false),
            Attribute::Rightline => self.set_rightline(false),
            Attribute::Strikeout => self.set_strikeout(false),
            Attribute::Underline => self.set_underline(false),
        }
        self
    }
}

/// Implement the | operator for adding Attributes to Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse;
/// attributes = attributes | other;
/// assert!(attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(!attributes.is_italic());
/// ```
impl BitOr for Attributes {
    type Output = Attributes;

    fn bitor(self, rhs: Attributes) -> Attributes {
        Attributes {
            raw: self.raw | rhs.raw,
            color_pair: ColorPair(self.color_pair.0 | rhs.color_pair.0),
        }
    }
}

/// Implement the ^ operator for removing Attributes from Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let mut attributes = Attributes::new() | Attribute::Blink | Attribute::Bold;
/// let other = Attributes::new() | Attribute::Reverse | Attribute::Bold;
/// attributes = attributes ^ other;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_reverse());
/// assert!(attributes.is_blink());
/// ```
impl BitXor for Attributes {
    type Output = Attributes;

    fn bitxor(self, rhs: Attributes) -> Attributes {
        Attributes {
            raw: self.raw ^ rhs.raw,
            color_pair: ColorPair(self.color_pair.0 ^ rhs.color_pair.0),
        }
    }
}

/// Implement the | operator for combining two 'Attribute's into Attributes
///
/// # Example
///
/// ```
/// use pancurses::{Attribute, Attributes};
///
/// let attributes = Attribute::Blink | Attribute::Reverse;
/// assert!(!attributes.is_bold());
/// assert!(attributes.is_blink());
/// assert!(attributes.is_reverse());
/// ```
impl BitOr for Attribute {
    type Output = Attributes;

    fn bitor(self, rhs: Attribute) -> Attributes {
        Attributes::new() | self | rhs
    }
}

/// Implement Default for Attributes
///
/// # Example
///
/// ```
/// use pancurses::Attributes;
/// let attributes: Attributes = Default::default();
/// assert_eq!(attributes, Attributes::new());
/// ```
impl Default for Attributes {
    fn default() -> Self {
        Self::new()
    }
}

impl From<Attribute> for Attributes {
    fn from(attribute: Attribute) -> Attributes {
        Attributes::new() | attribute
    }
}

impl From<Attribute> for chtype {
    fn from(attribute: Attribute) -> chtype {
        chtype::from(Attributes::from(attribute))
    }
}

impl From<Attributes> for chtype {
    fn from(attributes: Attributes) -> chtype {
        attributes.raw
    }
}
<|endoftext|>


<commit_before>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate docopt;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{
    Catalog,
    CatalogVersion,
    Collection,
    Folders,
    Image,
    Keyword, KeywordTree,
    LibraryFile,
    LrId,
    LrObject
};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String)
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump =>
                process_dump(&args),
            Command::Audit =>
                process_audit(&args),
            _ =>
                ()
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!("\tVersion: {} ({:?})", catalog.version,
                 catalog.catalog_version);
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        if catalog.catalog_version != CatalogVersion::Lr4 {
            println!("Unsupported catalog version");
            return;
        }
        {
            let root_keyword_id = catalog.root_keyword_id;
            let keywordtree = catalog.load_keywords_tree();
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(root_keyword_id, &keywords, &keywordtree);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn print_keyword(level: i32, id: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    if let Some(keyword) = keywords.get(&id) {
        let mut indent = String::from("");
        if level > 0 {
            for _ in 0..level-1 {
                indent.push(' ');
            }
            indent.push_str("+ ")
        }
        println!("| {:>7} | {} | {:>7} | {}{}", keyword.id(), keyword.uuid(),
                 keyword.parent, indent, keyword.name);
        let children = tree.children_for(id);
        for child in children {
            print_keyword(level + 1, child, keywords, tree);
        }
    }
}

fn dump_keywords(root: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    println!("Keywords");
    println!("+---------+--------------------------------------+---------+----------------------------");
    println!("| id      | uuid                                 | parent  | name");
    println!("+---------+--------------------------------------+---------+----------------------------");
    print_keyword(0, root, keywords, tree);
    println!("+---------+--------------------------------------+---------+----------------------------");
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!(" id      + uuid                                 + name             + absolute path");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!("+ {:>6} + {} + {:<16} + {:<26}", root.id(), root.uuid(), root.name, root.absolute_path);
    }
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+--------+--------------------------------------+--------+----------------------------");
    println!("+ id     + uuid                                 + root   + path");
    println!("+--------+--------------------------------------+--------+----------------------------");
    for folder in &folders.folders {
        println!("+ {:>6} + {} + {:>6} + {:<26} + {:?}",
                 folder.id(), folder.uuid(), folder.root_folder,
                 folder.path_from_root, folder.content);
    }
    println!("+--------+--------------------------------------+--------+----------------------------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    println!(" id      + uuid                                 + folder + extens + basename         + sidecars +");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    for libfile in libfiles {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<16} + {:<8} +",
                 libfile.id(), libfile.uuid(), libfile.folder,
                 libfile.extension, libfile.basename,
                 libfile.sidecar_extensions);
    }
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    println!(" id      + uuid                                 + root   + format + or + P +");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    for image in images {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<2} + {} +",
                 image.id(), image.uuid(), image.root_file,
                 image.file_format,
                 image.orientation.as_ref().unwrap_or(&String::from("")),
                 image.pick);
    }
    println!("+--------+--------------------------------------+--------+--------+----+----+");

}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+--------+--------------------------------------+--------+------");
    println!(" id      + name                                 + parent +");
    println!("+--------+--------------------------------------+--------+------");
    for collection in collections {
        println!("+ {:>6} + {:<36} + {:>6} + {}",
                 collection.id(), collection.name, collection.parent,
                 collection.system_only)
    }
    println!("+--------+--------------------------------------+--------+------");

}

fn process_audit(_: &Args) {

}
<commit_msg>dumper: realign ids to 7<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate docopt;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{
    Catalog,
    CatalogVersion,
    Collection,
    Folders,
    Image,
    Keyword, KeywordTree,
    LibraryFile,
    LrId,
    LrObject
};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String)
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump =>
                process_dump(&args),
            Command::Audit =>
                process_audit(&args),
            _ =>
                ()
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!("\tVersion: {} ({:?})", catalog.version,
                 catalog.catalog_version);
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        if catalog.catalog_version != CatalogVersion::Lr4 {
            println!("Unsupported catalog version");
            return;
        }
        {
            let root_keyword_id = catalog.root_keyword_id;
            let keywordtree = catalog.load_keywords_tree();
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(root_keyword_id, &keywords, &keywordtree);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn print_keyword(level: i32, id: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    if let Some(keyword) = keywords.get(&id) {
        let mut indent = String::from("");
        if level > 0 {
            for _ in 0..level-1 {
                indent.push(' ');
            }
            indent.push_str("+ ")
        }
        println!("| {:>7} | {} | {:>7} | {}{}", keyword.id(), keyword.uuid(),
                 keyword.parent, indent, keyword.name);
        let children = tree.children_for(id);
        for child in children {
            print_keyword(level + 1, child, keywords, tree);
        }
    }
}

fn dump_keywords(root: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    println!("Keywords");
    println!("+---------+--------------------------------------+---------+----------------------------");
    println!("| id      | uuid                                 | parent  | name");
    println!("+---------+--------------------------------------+---------+----------------------------");
    print_keyword(0, root, keywords, tree);
    println!("+---------+--------------------------------------+---------+----------------------------");
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("| id      | uuid                                 | name             | absolute path");
    println!("+---------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!("| {:>7} | {} | {:<16} | {:<26}", root.id(), root.uuid(), root.name, root.absolute_path);
    }
    println!("+---------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+---------+--------------------------------------+--------+----------------------------");
    println!("| id      | uuid                                 | root   | path");
    println!("+---------+--------------------------------------+--------+----------------------------");
    for folder in &folders.folders {
        println!("| {:>7} | {} | {:>7} | {:<26} | {:?}",
                 folder.id(), folder.uuid(), folder.root_folder,
                 folder.path_from_root, folder.content);
    }
    println!("+---------+--------------------------------------+--------+----------------------------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Images");
    println!("+---------+--------------------------------------+---------+--------+------------------+----------+");
    println!("| id      | uuid                                 | folder  | extens | basename         | sidecars |");
    println!("+---------+--------------------------------------+---------+--------+------------------+----------+");
    for libfile in libfiles {
        println!("| {:>7} | {} | {:>7} | {:<6} | {:<16} | {:<8} |",
                 libfile.id(), libfile.uuid(), libfile.folder,
                 libfile.extension, libfile.basename,
                 libfile.sidecar_extensions);
    }
    println!("+---------+--------------------------------------+---------+--------+------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+---------+--------------------------------------+---------+--------+----+----+");
    println!("| id      | uuid                                 | root    | format | or | P  |");
    println!("+---------+--------------------------------------+---------+--------+----+----+");
    for image in images {
        println!("| {:>7} | {} | {:>7} | {:<6} | {:<2} | {} |",
                 image.id(), image.uuid(), image.root_file,
                 image.file_format,
                 image.orientation.as_ref().unwrap_or(&String::from("")),
                 image.pick);
    }
    println!("+---------+--------------------------------------+---------+--------+----+----+");

}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+---------+--------------------------------------+---------+------");
    println!("| id      | name                                 | parent  |");
    println!("+---------+--------------------------------------+---------+------");
    for collection in collections {
        println!("| {:>7} | {:<36} | {:>7} | {}",
                 collection.id(), collection.name, collection.parent,
                 collection.system_only)
    }
    println!("+---------+--------------------------------------+---------+------");

}

fn process_audit(_: &Args) {

}
<|endoftext|>