<commit_before>use super::super::{Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub struct GeneratorOption {
    pub clue_constraint: Grid<ClueConstraint>,
    pub max_clue: Option<i32>,
    pub use_trial_and_error: bool,
}

pub fn generate<R: Rng>(
    opts: &GeneratorOption,
    dic: &Dictionary,
    consecutive_dic: &ConsecutiveRegionDictionary,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = opts.clue_constraint.height();
    let width = opts.clue_constraint.width();
    let mut has_clue = Grid::new(height, width, false);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, dic, consecutive_dic);
    let mut current_energy = 0i32;
    let mut n_clues = 0;

    for y in 0..height {
        for x in 0..width {
            if opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue[(Y(y), X(x))] = true;
                n_clues += 1;
            }
        }
    }

    let n_step = height * width * 10;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                let y2 = height - 1 - y;
                let x2 = width - 1 - x;
                if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                    && (y != y2 || x != x2)
                {
                    continue;
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.is_valid_coord((Y(y + dy), X(x + dx)))
                            && has_clue[(Y(y + dy), X(x + dx))]
                        {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut updated = false;

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let mut n_clues2 = n_clues;
            let (Y(y), X(x)) = loc;
            let loc2 = (Y(height - 1 - y), X(width - 1 - x));
            if clue == NO_CLUE {
                if loc == loc2 {
                    n_clues2 -= 1;
                } else if problem[loc2] == NO_CLUE {
                    n_clues2 -= 2;
                }
            } else {
                if !has_clue[loc] {
                    n_clues2 += 1;
                }
                if loc != loc2 && !has_clue[loc2] {
                    n_clues2 += 1;
                }
            }
            if let Some(max_clue) = opts.max_clue {
                if max_clue < n_clues2 {
                    continue;
                }
            }

            problem[loc] = clue;
            let next_field = if previous_clue == NO_CLUE {
                let mut f = field.clone();
                f.add_clue(loc, clue);
                f.solve();
                if opts.use_trial_and_error {
                    f.trial_and_error();
                }
                f
            } else {
                solve_test(
                    &problem,
                    &has_clue,
                    opts.use_trial_and_error,
                    dic,
                    consecutive_dic,
                )
            };
            let energy = next_field.decided_cells() - 4 * n_clues2;

            let update = !next_field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                current_energy = energy;
                n_clues = n_clues2;
                field = next_field;

                let loc2 = (Y(height - 1 - y), X(width - 1 - x));
                if clue == NO_CLUE {
                    if problem[loc2] == NO_CLUE {
                        has_clue[loc] = false;
                        has_clue[loc2] = false;
                    }
                } else {
                    has_clue[loc] = true;
                    has_clue[loc2] = true;
                }

                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }
                updated = true;
                break;
            }
            problem[loc] = previous_clue;
        }

        if !updated {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &Grid<bool>,
    use_trial_and_error: bool,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue[(Y(y), X(x))] {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    if use_trial_and_error {
        ret.trial_and_error();
    }

    ret
}
<commit_msg>Tapa: add symmetry option to generator<commit_after>use super::super::{Coord, Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub struct GeneratorOption {
    pub clue_constraint: Grid<ClueConstraint>,
    pub max_clue: Option<i32>,
    pub symmetry: bool,
    pub use_trial_and_error: bool,
}

enum HasClueHistory {
    Update(Coord, bool),
    Checkpoint,
}
struct HasClue {
    has_clue: Grid<bool>,
    n_clues: i32,
    history: Vec<HasClueHistory>,
}

impl HasClue {
    fn new(height: i32, width: i32) -> HasClue {
        HasClue {
            has_clue: Grid::new(height, width, false),
            n_clues: 0,
            history: vec![],
        }
    }

    fn update(&mut self, loc: Coord, val: bool) {
        if self.has_clue[loc] == val {
            return;
        }
        if val {
            self.n_clues += 1;
        } else {
            self.n_clues -= 1;
        }
        self.history.push(HasClueHistory::Update(loc, !val));
        self.has_clue[loc] = val;
    }
    fn get(&self, loc: Coord) -> bool {
        self.has_clue[loc]
    }
    fn get_checked(&self, loc: Coord) -> bool {
        self.has_clue.is_valid_coord(loc) && self.has_clue[loc]
    }
    fn add_checkpoint(&mut self) {
        self.history.push(HasClueHistory::Checkpoint);
    }
    fn rollback(&mut self) {
        loop {
            match self.history.pop() {
                Some(HasClueHistory::Update(cd, v)) => {
                    self.has_clue[cd] = v;
                    if v {
                        self.n_clues += 1;
                    } else {
                        self.n_clues -= 1;
                    }
                }
                _ => break,
            }
        }
    }
    fn forget_history(&mut self) {
        self.history.clear();
    }
}

pub fn generate<R: Rng>(
    opts: &GeneratorOption,
    dic: &Dictionary,
    consecutive_dic: &ConsecutiveRegionDictionary,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = opts.clue_constraint.height();
    let width = opts.clue_constraint.width();
    let mut has_clue = HasClue::new(height, width);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, dic, consecutive_dic);
    let mut current_energy = 0i32;

    for y in 0..height {
        for x in 0..width {
            if opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue.update((Y(y), X(x)), true);
            }
        }
    }

    let n_step = height * width * 10;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                if opts.symmetry {
                    let y2 = height - 1 - y;
                    let x2 = width - 1 - x;
                    if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                        && (y != y2 || x != x2)
                    {
                        continue;
                    }
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.get_checked((Y(y + dy), X(x + dx))) {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && opts.clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut updated = false;

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let (Y(y), X(x)) = loc;

            if opts.symmetry {
                let loc2 = (Y(height - 1 - y), X(width - 1 - x));
                if clue == NO_CLUE {
                    if problem[loc2] == NO_CLUE {
                        has_clue.update(loc, false);
                        has_clue.update(loc2, false);
                    }
                } else {
                    has_clue.update(loc, true);
                    has_clue.update(loc2, true);
                }
            } else {
                has_clue.update(loc, clue != NO_CLUE);
            }
            if let Some(max_clue) = opts.max_clue {
                if max_clue < has_clue.n_clues {
                    continue;
                }
            }

            problem[loc] = clue;
            let next_field = if previous_clue == NO_CLUE {
                let mut f = field.clone();
                f.add_clue(loc, clue);
                f.solve();
                if opts.use_trial_and_error {
                    f.trial_and_error();
                }
                f
            } else {
                solve_test(
                    &problem,
                    &has_clue,
                    opts.use_trial_and_error,
                    dic,
                    consecutive_dic,
                )
            };
            let energy = next_field.decided_cells() - 4 * has_clue.n_clues;

            let update = !next_field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                current_energy = energy;
                field = next_field;

                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue.get((Y(y), X(x))) && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }
                updated = true;
                has_clue.forget_history();
                break;
            }
            problem[loc] = previous_clue;
            has_clue.rollback();
        }

        if !updated {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &HasClue,
    use_trial_and_error: bool,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue.get((Y(y), X(x))) {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    if use_trial_and_error {
        ret.trial_and_error();
    }

    ret
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate rustbox;

use std::collections::dlist::DList;
use std::comm::{Receiver, Sender};
use std::io::{File, BufferedReader};
use std::str::from_utf8;

pub struct Editor {
    pub sender: Sender<rustbox::Event>,
    pub events: Receiver<rustbox::Event>,
    pub buffers: Vec<Buffer>,
    pub cursor_x: int,
    pub cursor_y: int,
}

pub struct Buffer {
    pub lines: DList<Line>,
    pub active: bool,
    pub num_lines: int,
}

#[deriving(Clone)]
pub struct Line {
    pub data: String,
}

pub enum Response {
    Continue,
    Quit,
}

impl Buffer {
    pub fn new() -> Buffer {
        Buffer {
            lines: DList::new(),
            active: false,
            num_lines: 0,
        }
    }

    pub fn new_from_file(filename: String) -> Buffer {
        let path = Path::new(filename.to_string());

        let mut new_buffer = Buffer::new();
        let mut file = BufferedReader::new(File::open(&path));
        let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();

        for line in lines.iter() {
            new_buffer.lines.push(Line{data: line.clone()})
        }

        new_buffer
    }
}

impl Line {
    pub fn new() -> Line {
        Line {
            data: String::new(),
        }
    }
}

impl Editor {
    pub fn new(filenames: &[String]) -> Editor {
        let mut buffers = Vec::new();

        for filename in filenames.iter() {
            let mut b = Buffer::new_from_file(filename.clone());
            b.active = true;
            buffers.push(b);
        }

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            buffers: buffers,
            cursor_x: 0,
            cursor_y: 0,
        }
    }

    pub fn handle_key_event(&mut self, ch: u32) -> Response {
        match std::char::from_u32(ch) {
            Some('q') => Quit,

            // cursor movement
            Some('h') => { self.cursor_x -= 1; Continue },
            Some('j') => { self.cursor_y += 1; Continue },
            Some('k') => { self.cursor_y -= 1; Continue },
            Some('l') => { self.cursor_x += 1; Continue },

            // default
            _ => Continue,
        }
    }

    pub fn draw(&mut self) {
        // TODO: change this to only draw the active buffer
        for buffer in self.buffers.iter() {
            if buffer.active {
                for (index, line) in buffer.lines.iter().enumerate() {
                    rustbox::print(0, index, rustbox::Bold, rustbox::White, rustbox::Black, line.data.clone());
                }
            }
        }
        rustbox::set_cursor(self.cursor_x, self.cursor_y);
    }

    pub fn start(&mut self) -> bool {
        loop {
            self.draw();
            rustbox::present();
            match self.events.try_recv() {
                Ok(rustbox::KeyEvent(_, _, ch)) => {
                    match self.handle_key_event(ch) {
                        Quit => break,
                        _ => {}
                    }
                },
                TryRecvError => {}
            }
        }
        return false
    }

}

<commit_msg>Resolve compile warnings<commit_after>#![feature(macro_rules)]

extern crate rustbox;

use std::collections::dlist::DList;
use std::comm::{Receiver, Sender};
use std::io::{File, BufferedReader};

pub struct Editor {
    pub sender: Sender<rustbox::Event>,
    pub events: Receiver<rustbox::Event>,
    pub buffers: Vec<Buffer>,
    pub cursor_x: int,
    pub cursor_y: int,
}

pub struct Buffer {
    pub lines: DList<Line>,
    pub active: bool,
    pub num_lines: int,
}

#[deriving(Clone)]
pub struct Line {
    pub data: String,
}

pub enum Response {
    Continue,
    Quit,
}

impl Buffer {
    pub fn new() -> Buffer {
        Buffer {
            lines: DList::new(),
            active: false,
            num_lines: 0,
        }
    }

    pub fn new_from_file(filename: String) -> Buffer {
        let path = Path::new(filename.to_string());

        let mut new_buffer = Buffer::new();
        let mut file = BufferedReader::new(File::open(&path));
        let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();

        for line in lines.iter() {
            new_buffer.lines.push(Line{data: line.clone()})
        }

        new_buffer
    }
}

impl Line {
    pub fn new() -> Line {
        Line {
            data: String::new(),
        }
    }
}

impl Editor {
    pub fn new(filenames: &[String]) -> Editor {
        let mut buffers = Vec::new();

        for filename in filenames.iter() {
            let mut b = Buffer::new_from_file(filename.clone());
            b.active = true;
            buffers.push(b);
        }

        let (send, recv) = channel();
        Editor {
            sender: send,
            events: recv,
            buffers: buffers,
            cursor_x: 0,
            cursor_y: 0,
        }
    }

    pub fn handle_key_event(&mut self, ch: u32) -> Response {
        match std::char::from_u32(ch) {
            Some('q') => Quit,

            // cursor movement
            Some('h') => { self.cursor_x -= 1; Continue },
            Some('j') => { self.cursor_y += 1; Continue },
            Some('k') => { self.cursor_y -= 1; Continue },
            Some('l') => { self.cursor_x += 1; Continue },

            // default
            _ => Continue,
        }
    }

    pub fn draw(&mut self) {
        // TODO: change this to only draw the active buffer
        for buffer in self.buffers.iter() {
            if buffer.active {
                for (index, line) in buffer.lines.iter().enumerate() {
                    rustbox::print(0, index, rustbox::Bold, rustbox::White, rustbox::Black, line.data.clone());
                }
            }
        }
        rustbox::set_cursor(self.cursor_x, self.cursor_y);
    }

    pub fn start(&mut self) -> bool {
        loop {
            self.draw();
            rustbox::present();
            match self.events.try_recv() {
                Ok(rustbox::KeyEvent(_, _, ch)) => {
                    match self.handle_key_event(ch) {
                        Quit => break,
                        _ => {}
                    }
                },
                _ => {}
            }
        }
        return false
    }

}

<|endoftext|>


<commit_before>use core::lexer;
use core::lexer::types::*;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use std::result;

pub fn main() {
    println!("The Monkey programming language REPL");

    let mut editor: Editor<()> = Editor::new();

    loop {
        match editor.readline("> ") {
            Ok(input) => {
                match handle_input(input) {
                    Ok(output) => println!("{}", output),
                    Err(ReplError::DoNothing) => continue,
                    Err(ReplError::Exit) => break,
                    Err(ReplError::LexError(err)) => println!("Lexical error: {:?}", err),
                }
            }
            Err(ReadlineError::Interrupted) => continue,
            Err(ReadlineError::Eof) => break,
            Err(err) => println!("{:?}", err),
        }
    }
    println!("bye!");
}

type Result<T> = result::Result<T, ReplError>;

enum ReplError {
    Exit,
    DoNothing,
    LexError(LexError),
}

fn handle_input(input: String) -> Result<String> {
    if input.len() == 0 {
        return Err(ReplError::DoNothing);
    } else if input == "exit" {
        return Err(ReplError::Exit);
    }

    let tokens = try!(lexer::tokenize(input.chars()).map_err(|err| ReplError::LexError(err)));

    Ok(format!("{:?}", tokens))
}
<commit_msg>Change repl to show AST<commit_after>use core::lexer;
use core::lexer::types::*;
use core::parser;
use core::parser::types::*;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use std::result;

pub fn main() {
    println!("The Monkey programming language REPL");

    let mut editor: Editor<()> = Editor::new();

    loop {
        match editor.readline("> ") {
            Ok(input) => {
                match handle_input(input) {
                    Ok(output) => println!("{}", output),
                    Err(ReplError::DoNothing) => continue,
                    Err(ReplError::Exit) => break,
                    Err(ReplError::LexError(err)) => println!("Lexical error: {:?}", err),
                    Err(ReplError::ParseError(err)) => println!("Parse error: {:?}", err),
                }
            }
            Err(ReadlineError::Interrupted) => continue,
            Err(ReadlineError::Eof) => break,
            Err(err) => println!("{:?}", err),
        }
    }
    println!("bye!");
}

type Result<T> = result::Result<T, ReplError>;

enum ReplError {
    Exit,
    DoNothing,
    LexError(LexError),
    ParseError(ParseError),
}

fn handle_input(input: String) -> Result<String> {
    if input.len() == 0 {
        return Err(ReplError::DoNothing);
    } else if input == "exit" {
        return Err(ReplError::Exit);
    }

    let tokens = try!(lexer::tokenize(input.chars()).map_err(|err| ReplError::LexError(err)));
    let ast = try!(parser::parse(tokens).map_err(|err| ReplError::ParseError(err)));

    Ok(format!("{:?}", ast))
}
<|endoftext|>


<commit_before>// mrusty. mruby bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use rl_sys::readline;
use rl_sys::history::{listmgmt, mgmt};

use super::mruby::*;

/// A `struct` that exposes an `MRuby` to a REPL. Requires `repl` build feature.
///
/// # Examples
///
/// ```no-run
/// # use mrusty::MRuby;
/// # use mrusty::Repl;
/// let mruby = MRuby::new();
/// let repl = Repl::new(mruby);
///
/// repl.start();
/// ```
#[cfg(feature = "repl")]
pub struct Repl {
    mruby: MRubyType
}

impl Repl {
    /// Creates a new `Repl`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::Repl;
    /// let mruby = MRuby::new();
    /// let repl = Repl::new(mruby);
    /// ```
    pub fn new(mruby: MRubyType) -> Repl {
        Repl {
            mruby: mruby
        }
    }

    /// Starts a `Repl`.
    ///
    /// Use `'\'` for multiline statements.
    ///
    /// # Examples
    ///
    /// ```no-run
    /// # use mrusty::MRuby;
    /// # use mrusty::Repl;
    /// let mruby = MRuby::new();
    /// let repl = Repl::new(mruby);
    ///
    /// repl.start();
    /// ```
    pub fn start(&self) {
        let mut command = String::new();

        loop {
            self.mruby.filename("repl");

            let head = if command.is_empty() {
                "mrusty> "
            } else {
                "mrusty* "
            };

            let input = match readline::readline(head) {
                Ok(Some(s)) => s,
                Ok(None) => break,
                Err(e) => {
                    println!("{}", e);

                    break
                }
            };

            if input.ends_with("\\") {
                let trimmed = input.trim_right_matches("\\");

                command = command + trimmed + "\n";
                listmgmt::add(&trimmed).unwrap();

                continue
            } else {
                listmgmt::add(&input).unwrap();
            }

            if command == "" {
                command = input;
            } else {
                command = command + &input;
            }

            match self.mruby.run(&command) {
                Ok(value) => {
                    let result = value.call("to_s", vec![]).unwrap().to_str().unwrap();

                    println!("{}", result);
                },
                Err(message) => {
                    println!("{}", message);
                }
            }

            if !command.is_empty() {
                command = String::new();
            }
        }

        mgmt::cleanup();

        println!("");
    }
}
<commit_msg>Added Repl renaming. Fixes #33.<commit_after>// mrusty. mruby bindings for Rust
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use rl_sys::readline;
use rl_sys::history::{listmgmt, mgmt};

use super::mruby::*;

/// A `struct` that exposes an `MRuby` to a REPL. Requires `repl` build feature.
///
/// # Examples
///
/// ```no-run
/// # use mrusty::MRuby;
/// # use mrusty::Repl;
/// let mruby = MRuby::new();
/// let repl = Repl::new(mruby);
///
/// repl.start();
/// ```
#[cfg(feature = "repl")]
pub struct Repl {
    mruby: MRubyType,
    name: String
}

impl Repl {
    /// Creates a new `Repl`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::Repl;
    /// let mruby = MRuby::new();
    /// let repl = Repl::new(mruby);
    /// ```
    pub fn new(mruby: MRubyType) -> Repl {
        Repl {
            mruby: mruby,
            name: "mrusty".to_string()
        }
    }

    /// Renames a `Repl`. The command line will start with `{name}> `.
    ///
    /// # Examples
    ///
    /// ```
    /// # use mrusty::MRuby;
    /// # use mrusty::Repl;
    /// let mruby = MRuby::new();
    /// let mut repl = Repl::new(mruby);
    ///
    /// repl.rename("repl");
    /// ```
    pub fn rename(&mut self, name: &str) {
        self.name = name.to_string();
    }

    /// Starts a `Repl`.
    ///
    /// Use `'\'` for multiline statements.
    ///
    /// # Examples
    ///
    /// ```no-run
    /// # use mrusty::MRuby;
    /// # use mrusty::Repl;
    /// let mruby = MRuby::new();
    /// let repl = Repl::new(mruby);
    ///
    /// repl.start();
    /// ```
    pub fn start(&self) {
        let mut command = String::new();

        let single = self.name.clone() + "> ";
        let multi  = self.name.clone() + "* ";

        loop {
            self.mruby.filename("repl");

            let head = if command.is_empty() {
                &single
            } else {
                &multi
            };

            let input = match readline::readline(head) {
                Ok(Some(s)) => s,
                Ok(None) => break,
                Err(e) => {
                    println!("{}", e);

                    break
                }
            };

            if input.ends_with("\\") {
                let trimmed = input.trim_right_matches("\\");

                command = command + trimmed + "\n";
                listmgmt::add(&trimmed).unwrap();

                continue
            } else {
                listmgmt::add(&input).unwrap();
            }

            if command == "" {
                command = input;
            } else {
                command = command + &input;
            }

            match self.mruby.run(&command) {
                Ok(value) => {
                    let result = value.call("to_s", vec![]).unwrap().to_str().unwrap();

                    println!("{}", result);
                },
                Err(message) => {
                    println!("{}", message);
                }
            }

            if !command.is_empty() {
                command = String::new();
            }
        }

        mgmt::cleanup();

        println!("");
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;

pub fn general() {
    io::println("Usage: rustpkg [options] <cmd> [args..]

Where <cmd> is one of:
    build, clean, do, info, install, list, prefer, test, uninstall, unprefer

Options:

    -h, --help                  Display this message
    --sysroot PATH              Override the system root
    <cmd> -h, <cmd> --help      Display help for <cmd>");
}

pub fn build() {
    io::println("rustpkg build [options..] [package-ID]

Build the given package ID if specified. With no package ID argument,
build the package in the current directory. In that case, the current
directory must be a direct child of an `src` directory in a workspace.

Options:
    -c, --cfg      Pass a cfg flag to the package script
    --no-link      Compile and assemble, but don't link (like -c in rustc)
    --no-trans     Parse and translate, but don't generate any code
    --pretty       Pretty-print the code, but don't generate output
    --parse-only   Parse the code, but don't typecheck or generate code
    -S             Generate assembly code, but don't assemble or link it
    -S --emit-llvm Generate LLVM assembly code
    --emit-llvm    Generate LLVM bitcode
    --linker PATH  Use a linker other than the system linker
    --link-args [ARG..] Extra arguments to pass to the linker
    --opt-level=n  Set the optimization level (0 <= n <= 3)
    -O             Equivalent to --opt-level=2
    --save-temps   Don't delete temporary files
    --target TRIPLE Set the target triple
    --target-cpu CPU Set the target CPU
    -Z FLAG        Enable an experimental rustc feature (see `rustc --help`)");
}

pub fn clean() {
    io::println("rustpkg clean

Remove all build files in the work cache for the package in the current
directory.");
}

pub fn do_cmd() {
    io::println("rustpkg do <cmd>

Runs a command in the package script. You can listen to a command
by tagging a function with the attribute `#[pkg_do(cmd)]`.");
}

pub fn info() {
    io::println("rustpkg [options..] info

Probe the package script in the current directory for information.

Options:
    -j, --json      Output the result as JSON");
}

pub fn list() {
    io::println("rustpkg list

List all installed packages.");
}

pub fn install() {
    io::println("rustpkg install [options..] [package-ID]

Install the given package ID if specified. With no package ID
argument, install the package in the current directory.
In that case, the current directory must be a direct child of a
`src` directory in a workspace.

Examples:
    rustpkg install
    rustpkg install github.com/mozilla/servo
    rustpkg install github.com/mozilla/servo#0.1.2

Options:
    -c, --cfg      Pass a cfg flag to the package script
    --emit-llvm    Generate LLVM bitcode
    --linker PATH  Use a linker other than the system linker
    --link-args [ARG..] Extra arguments to pass to the linker
    --opt-level=n  Set the optimization level (0 <= n <= 3)
    -O             Equivalent to --opt-level=2
    --save-temps   Don't delete temporary files
    --target TRIPLE Set the target triple
    --target-cpu CPU Set the target CPU
    -Z FLAG        Enable an experimental rustc feature (see `rustc --help`)");
}

pub fn uninstall() {
    io::println("rustpkg uninstall <id|name>[@version]

Remove a package by id or name and optionally version. If the package(s)
is/are depended on by another package then they cannot be removed.");
}

pub fn prefer() {
    io::println("rustpkg [options..] prefer <id|name>[@version]

By default all binaries are given a unique name so that multiple versions can
coexist. The prefer command will symlink the uniquely named binary to
the binary directory under its bare name. If version is not supplied, the
latest version of the package will be preferred.

Example:
    export PATH=$PATH:/home/user/.rustpkg/bin
    rustpkg prefer machine@1.2.4
    machine -v
    ==> v1.2.4
    rustpkg prefer machine@0.4.6
    machine -v
    ==> v0.4.6");
}

pub fn unprefer() {
    io::println("rustpkg [options..] unprefer <id|name>[@version]

Remove all symlinks from the store to the binary directory for a package
name and optionally version. If version is not supplied, the latest version
of the package will be unpreferred. See `rustpkg prefer -h` for more
information.");
}

pub fn test() {
    io::println("rustpkg [options..] test

Build all targets described in the package script in the current directory
with the test flag. The test bootstraps will be run afterwards and the output
and exit code will be redirected.

Options:
    -c, --cfg      Pass a cfg flag to the package script");
}

pub fn init() {
    io::println("rustpkg init name

This makes a new workspace for working on a project named name.
");
}
<commit_msg>auto merge of #9337 : steveklabnik/rust/rustpkg_usage, r=catamorphism<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io;

pub fn general() {
    io::println("Usage: rustpkg [options] <cmd> [args..]

Where <cmd> is one of:
    build, clean, do, info, install, list, prefer, test, uninstall, unprefer

Options:

    -h, --help                  Display this message
    --sysroot PATH              Override the system root
    <cmd> -h, <cmd> --help      Display help for <cmd>");
}

pub fn build() {
    io::println("rustpkg build [options..] [package-ID]

Build the given package ID if specified. With no package ID argument,
build the package in the current directory. In that case, the current
directory must be a direct child of an `src` directory in a workspace.

Options:
    -c, --cfg      Pass a cfg flag to the package script
    --no-link      Compile and assemble, but don't link (like -c in rustc)
    --no-trans     Parse and translate, but don't generate any code
    --pretty       Pretty-print the code, but don't generate output
    --parse-only   Parse the code, but don't typecheck or generate code
    -S             Generate assembly code, but don't assemble or link it
    -S --emit-llvm Generate LLVM assembly code
    --emit-llvm    Generate LLVM bitcode
    --linker PATH  Use a linker other than the system linker
    --link-args [ARG..] Extra arguments to pass to the linker
    --opt-level=n  Set the optimization level (0 <= n <= 3)
    -O             Equivalent to --opt-level=2
    --save-temps   Don't delete temporary files
    --target TRIPLE Set the target triple
    --target-cpu CPU Set the target CPU
    -Z FLAG        Enable an experimental rustc feature (see `rustc --help`)");
}

pub fn clean() {
    io::println("rustpkg clean

Remove all build files in the work cache for the package in the current
directory.");
}

pub fn do_cmd() {
    io::println("rustpkg do <cmd>

Runs a command in the package script. You can listen to a command
by tagging a function with the attribute `#[pkg_do(cmd)]`.");
}

pub fn info() {
    io::println("rustpkg [options..] info

Probe the package script in the current directory for information.

Options:
    -j, --json      Output the result as JSON");
}

pub fn list() {
    io::println("rustpkg list

List all installed packages.");
}

pub fn install() {
    io::println("rustpkg install [options..] [package-ID]

Install the given package ID if specified. With no package ID
argument, install the package in the current directory.
In that case, the current directory must be a direct child of a
`src` directory in a workspace.

Examples:
    rustpkg install
    rustpkg install github.com/mozilla/servo
    rustpkg install github.com/mozilla/servo#0.1.2

Options:
    -c, --cfg      Pass a cfg flag to the package script
    --emit-llvm    Generate LLVM bitcode
    --linker PATH  Use a linker other than the system linker
    --link-args [ARG..] Extra arguments to pass to the linker
    --opt-level=n  Set the optimization level (0 <= n <= 3)
    -O             Equivalent to --opt-level=2
    --save-temps   Don't delete temporary files
    --target TRIPLE Set the target triple
    --target-cpu CPU Set the target CPU
    -Z FLAG        Enable an experimental rustc feature (see `rustc --help`)");
}

pub fn uninstall() {
    io::println("rustpkg uninstall <id|name>[@version]

Remove a package by id or name and optionally version. If the package(s)
is/are depended on by another package then they cannot be removed.");
}

pub fn prefer() {
    io::println("rustpkg [options..] prefer <id|name>[@version]

By default all binaries are given a unique name so that multiple versions can
coexist. The prefer command will symlink the uniquely named binary to
the binary directory under its bare name. If version is not supplied, the
latest version of the package will be preferred.

Example:
    export PATH=$PATH:/home/user/.rustpkg/bin
    rustpkg prefer machine@1.2.4
    machine -v
    ==> v1.2.4
    rustpkg prefer machine@0.4.6
    machine -v
    ==> v0.4.6");
}

pub fn unprefer() {
    io::println("rustpkg [options..] unprefer <id|name>[@version]

Remove all symlinks from the store to the binary directory for a package
name and optionally version. If version is not supplied, the latest version
of the package will be unpreferred. See `rustpkg prefer -h` for more
information.");
}

pub fn test() {
    io::println("rustpkg [options..] test

Build all targets described in the package script in the current directory
with the test flag. The test bootstraps will be run afterwards and the output
and exit code will be redirected.

Options:
    -c, --cfg      Pass a cfg flag to the package script");
}

pub fn init() {
    io::println("rustpkg init

This will turn the current working directory into a workspace. The first
command you run when starting off a new project.
");
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::ffi::CString;
use std::ops::Range;
use std::marker;
use std::path::Path;
use std::str;
use libc;

use {raw, Oid, Repository, Error, Object, ObjectType};
use util::{Binding, IntoCString};

/// A structure to represent a git [tree][1]
///
/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
pub struct Tree<'repo> {
    raw: *mut raw::git_tree,
    _marker: marker::PhantomData<Object<'repo>>,
}

/// A structure representing an entry inside of a tree. An entry is borrowed
/// from a tree.
pub struct TreeEntry<'tree> {
    raw: *mut raw::git_tree_entry,
    owned: bool,
    _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
}

/// An iterator over the entries in a tree.
pub struct TreeIter<'tree> {
    range: Range<usize>,
    tree: &'tree Tree<'tree>,
}

impl<'repo> Tree<'repo> {
    /// Get the id (SHA1) of a repository object
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
    }

    /// Get the number of entries listed in this tree.
    pub fn len(&self) -> usize {
        unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
    }

    /// Returns an iterator over the entries in this tree.
    pub fn iter(&self) -> TreeIter {
        TreeIter { range: 0..self.len(), tree: self }
    }

    /// Lookup a tree entry by SHA value.
    pub fn get_id(&self, id: Oid) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its position in the tree
    pub fn get(&self, n: usize) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byindex(&*self.raw(),
                                                  n as libc::size_t);
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its filename
    pub fn get_name(&self, filename: &str) -> Option<TreeEntry> {
        let filename = CString::new(filename).unwrap();
        unsafe {
            let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Retrieve a tree entry contained in a tree or in any of its subtrees,
    /// given its relative path.
    pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
        let path = try!(path.into_c_string());
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
            Ok(Binding::from_raw(ret))
        }
    }
}

impl<'repo> Binding for Tree<'repo> {
    type Raw = *mut raw::git_tree;

    unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
        Tree { raw: raw, _marker: marker::PhantomData }
    }
    fn raw(&self) -> *mut raw::git_tree { self.raw }
}

impl<'repo> Drop for Tree<'repo> {
    fn drop(&mut self) {
        unsafe { raw::git_tree_free(self.raw) }
    }
}

impl<'tree> TreeEntry<'tree> {
    /// Create a new tree entry from the raw pointer provided.
    ///
    /// The lifetime of the entry is tied to the tree provided and the function
    /// is unsafe because the validity of the pointer cannot be guaranteed.
    unsafe fn from_raw_const(raw: *const raw::git_tree_entry)
                                 -> TreeEntry<'tree> {
        TreeEntry {
            raw: raw as *mut raw::git_tree_entry,
            owned: false,
            _marker: marker::PhantomData,
        }
    }

    /// Get the id of the object pointed by the entry
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
    }

    /// Get the filename of a tree entry
    ///
    /// Returns `None` if the name is not valid utf-8
    pub fn name(&self) -> Option<&str> {
        str::from_utf8(self.name_bytes()).ok()
    }

    /// Get the filename of a tree entry
    pub fn name_bytes(&self) -> &[u8] {
        unsafe {
            ::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap()
        }
    }

    /// Convert a tree entry to the object it points to.
    pub fn to_object<'a>(&self, repo: &'a Repository)
                         -> Result<Object<'a>, Error> {
        let mut ret = 0 as *mut raw::git_object;
        unsafe {
            try_call!(raw::git_tree_entry_to_object(&mut ret, repo.raw(),
                                                    &*self.raw()));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Get the type of the object pointed by the entry
    pub fn kind(&self) -> Option<ObjectType> {
        ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
    }

    /// Get the UNIX file attributes of a tree entry
    pub fn filemode(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
    }

    /// Get the raw UNIX file attributes of a tree entry
    pub fn filemode_raw(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
    }
}

impl<'a> Binding for TreeEntry<'a> {
    type Raw = *mut raw::git_tree_entry;
    unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
        TreeEntry {
            raw: raw,
            owned: true,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_tree_entry { self.raw }
}

impl<'a> Clone for TreeEntry<'a> {
    fn clone(&self) -> TreeEntry<'a> {
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
            Binding::from_raw(ret)
        }
    }
}

impl<'a> PartialOrd for TreeEntry<'a> {
    fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<'a> Ord for TreeEntry<'a> {
    fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
        match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) } {
            0 => Ordering::Equal,
            n if n < 0 => Ordering::Less,
            _ => Ordering::Greater,
        }
    }
}

impl<'a> PartialEq for TreeEntry<'a> {
    fn eq(&self, other: &TreeEntry<'a>) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl<'a> Eq for TreeEntry<'a> {}

impl<'a> Drop for TreeEntry<'a> {
    fn drop(&mut self) {
        if self.owned {
            unsafe { raw::git_tree_entry_free(self.raw) }
        }
    }
}

impl<'tree> Iterator for TreeIter<'tree> {
    type Item = TreeEntry<'tree>;
    fn next(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next().and_then(|i| self.tree.get(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
    fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next_back().and_then(|i| self.tree.get(i))
    }
}
impl<'tree> ExactSizeIterator for TreeIter<'tree> {}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::prelude::*;
    use std::path::Path;

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();
        {
            let mut index = repo.index().unwrap();
            File::create(&td.path().join("foo")).unwrap().write_all(b"foo").unwrap();
            index.add_path(Path::new("foo")).unwrap();
            let id = index.write_tree().unwrap();
            let sig = repo.signature().unwrap();
            let tree = repo.find_tree(id).unwrap();
            let parent = repo.find_commit(repo.head().unwrap().target()
                                              .unwrap()).unwrap();
            repo.commit(Some("HEAD"), &sig, &sig, "another commit",
                        &tree, &[&parent]).unwrap();
        }
        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);
        let e1 = tree.get(0).unwrap();
        assert!(e1 == tree.get_id(e1.id()).unwrap());
        assert!(e1 == tree.get_name("foo").unwrap());
        assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
        assert_eq!(e1.name(), Some("foo"));
        e1.to_object(&repo).unwrap();

        repo.find_object(commit.tree_id(), None).unwrap().as_tree().unwrap();
    }
}
<commit_msg>add test to show problems with Tree::iter()<commit_after>use std::cmp::Ordering;
use std::ffi::CString;
use std::ops::Range;
use std::marker;
use std::path::Path;
use std::str;
use libc;

use {raw, Oid, Repository, Error, Object, ObjectType};
use util::{Binding, IntoCString};

/// A structure to represent a git [tree][1]
///
/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
pub struct Tree<'repo> {
    raw: *mut raw::git_tree,
    _marker: marker::PhantomData<Object<'repo>>,
}

/// A structure representing an entry inside of a tree. An entry is borrowed
/// from a tree.
pub struct TreeEntry<'tree> {
    raw: *mut raw::git_tree_entry,
    owned: bool,
    _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
}

/// An iterator over the entries in a tree.
pub struct TreeIter<'tree> {
    range: Range<usize>,
    tree: &'tree Tree<'tree>,
}

impl<'repo> Tree<'repo> {
    /// Get the id (SHA1) of a repository object
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
    }

    /// Get the number of entries listed in this tree.
    pub fn len(&self) -> usize {
        unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
    }

    /// Returns an iterator over the entries in this tree.
    pub fn iter(&self) -> TreeIter {
        TreeIter { range: 0..self.len(), tree: self }
    }

    /// Lookup a tree entry by SHA value.
    pub fn get_id(&self, id: Oid) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its position in the tree
    pub fn get(&self, n: usize) -> Option<TreeEntry> {
        unsafe {
            let ptr = raw::git_tree_entry_byindex(&*self.raw(),
                                                  n as libc::size_t);
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Lookup a tree entry by its filename
    pub fn get_name(&self, filename: &str) -> Option<TreeEntry> {
        let filename = CString::new(filename).unwrap();
        unsafe {
            let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
            if ptr.is_null() {
                None
            } else {
                Some(TreeEntry::from_raw_const(ptr))
            }
        }
    }

    /// Retrieve a tree entry contained in a tree or in any of its subtrees,
    /// given its relative path.
    pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
        let path = try!(path.into_c_string());
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
            Ok(Binding::from_raw(ret))
        }
    }
}

impl<'repo> Binding for Tree<'repo> {
    type Raw = *mut raw::git_tree;

    unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
        Tree { raw: raw, _marker: marker::PhantomData }
    }
    fn raw(&self) -> *mut raw::git_tree { self.raw }
}

impl<'repo> Drop for Tree<'repo> {
    fn drop(&mut self) {
        unsafe { raw::git_tree_free(self.raw) }
    }
}

impl<'tree> TreeEntry<'tree> {
    /// Create a new tree entry from the raw pointer provided.
    ///
    /// The lifetime of the entry is tied to the tree provided and the function
    /// is unsafe because the validity of the pointer cannot be guaranteed.
    unsafe fn from_raw_const(raw: *const raw::git_tree_entry)
                                 -> TreeEntry<'tree> {
        TreeEntry {
            raw: raw as *mut raw::git_tree_entry,
            owned: false,
            _marker: marker::PhantomData,
        }
    }

    /// Get the id of the object pointed by the entry
    pub fn id(&self) -> Oid {
        unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
    }

    /// Get the filename of a tree entry
    ///
    /// Returns `None` if the name is not valid utf-8
    pub fn name(&self) -> Option<&str> {
        str::from_utf8(self.name_bytes()).ok()
    }

    /// Get the filename of a tree entry
    pub fn name_bytes(&self) -> &[u8] {
        unsafe {
            ::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap()
        }
    }

    /// Convert a tree entry to the object it points to.
    pub fn to_object<'a>(&self, repo: &'a Repository)
                         -> Result<Object<'a>, Error> {
        let mut ret = 0 as *mut raw::git_object;
        unsafe {
            try_call!(raw::git_tree_entry_to_object(&mut ret, repo.raw(),
                                                    &*self.raw()));
            Ok(Binding::from_raw(ret))
        }
    }

    /// Get the type of the object pointed by the entry
    pub fn kind(&self) -> Option<ObjectType> {
        ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
    }

    /// Get the UNIX file attributes of a tree entry
    pub fn filemode(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
    }

    /// Get the raw UNIX file attributes of a tree entry
    pub fn filemode_raw(&self) -> i32 {
        unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
    }
}

impl<'a> Binding for TreeEntry<'a> {
    type Raw = *mut raw::git_tree_entry;
    unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
        TreeEntry {
            raw: raw,
            owned: true,
            _marker: marker::PhantomData,
        }
    }
    fn raw(&self) -> *mut raw::git_tree_entry { self.raw }
}

impl<'a> Clone for TreeEntry<'a> {
    fn clone(&self) -> TreeEntry<'a> {
        let mut ret = 0 as *mut raw::git_tree_entry;
        unsafe {
            assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
            Binding::from_raw(ret)
        }
    }
}

impl<'a> PartialOrd for TreeEntry<'a> {
    fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl<'a> Ord for TreeEntry<'a> {
    fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
        match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) } {
            0 => Ordering::Equal,
            n if n < 0 => Ordering::Less,
            _ => Ordering::Greater,
        }
    }
}

impl<'a> PartialEq for TreeEntry<'a> {
    fn eq(&self, other: &TreeEntry<'a>) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}
impl<'a> Eq for TreeEntry<'a> {}

impl<'a> Drop for TreeEntry<'a> {
    fn drop(&mut self) {
        if self.owned {
            unsafe { raw::git_tree_entry_free(self.raw) }
        }
    }
}

impl<'tree> Iterator for TreeIter<'tree> {
    type Item = TreeEntry<'tree>;
    fn next(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next().and_then(|i| self.tree.get(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
    fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
        self.range.next_back().and_then(|i| self.tree.get(i))
    }
}
impl<'tree> ExactSizeIterator for TreeIter<'tree> {}

#[cfg(test)]
mod tests {
    use {Repository,Tree,TreeEntry,ObjectType,Object};
    use tempdir::TempDir;
    use std::fs::File;
    use std::io::prelude::*;
    use std::path::Path;

    pub struct TestTreeIter<'a> {
        entries: Vec<TreeEntry<'a>>,
        repo: &'a Repository,
    }

    impl<'a> Iterator for TestTreeIter<'a> {
        type Item = TreeEntry<'a>;

        fn next(&mut self) -> Option<TreeEntry<'a> > {
            if self.entries.is_empty() {
                None
            } else {
                let entry = self.entries.remove(0);

                match entry.kind() {
                    Some(ObjectType::Tree) => {
                        let obj: Object<'a> = entry.to_object(self.repo).unwrap();

                        let tree: &Tree<'a> = obj.as_tree().unwrap();

                        for entry in tree.iter() {
                            self.entries.push(entry);
                        }
                    }
                    _ => {}
                }

                Some(entry)
            }
        }
    }

    fn tree_iter<'tree,'repo:'tree>(tree: &'tree Tree<'repo>, repo: &'repo Repository) -> TestTreeIter<'repo> {
        let mut initial = vec![];

        for entry in tree.iter() {
            initial.push(entry);
        }

        TestTreeIter {
            entries: initial,
            repo: repo,
        }
    }

    #[test]
    fn smoke_tree_iter() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);

//        let mut entries = vec![];

        for entry in tree_iter(&tree, &repo) {
            println!("iter entry {:?}", entry.name());
        }
    }

    fn setup_repo(td: &TempDir, repo: &Repository) {
        let mut index = repo.index().unwrap();
        File::create(&td.path().join("foo")).unwrap().write_all(b"foo").unwrap();
        index.add_path(Path::new("foo")).unwrap();
        let id = index.write_tree().unwrap();
        let sig = repo.signature().unwrap();
        let tree = repo.find_tree(id).unwrap();
        let parent = repo.find_commit(repo.head().unwrap().target()
                                      .unwrap()).unwrap();
        repo.commit(Some("HEAD"), &sig, &sig, "another commit",
                    &tree, &[&parent]).unwrap();
    }

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();

        setup_repo(&td, &repo);

        let head = repo.head().unwrap();
        let target = head.target().unwrap();
        let commit = repo.find_commit(target).unwrap();

        let tree = repo.find_tree(commit.tree_id()).unwrap();
        assert_eq!(tree.id(), commit.tree_id());
        assert_eq!(tree.len(), 1);
        let e1 = tree.get(0).unwrap();
        assert!(e1 == tree.get_id(e1.id()).unwrap());
        assert!(e1 == tree.get_name("foo").unwrap());
        assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
        assert_eq!(e1.name(), Some("foo"));
        e1.to_object(&repo).unwrap();

        repo.find_object(commit.tree_id(), None).unwrap().as_tree().unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the UART peripherals and USB serial devices.
//!
//! RPPAL controls the Raspberry Pi's main and auxiliary UART peripherals
//! through the ttyAMA0 and ttyS0 device interfaces. In addition to the built-in
//! UARTs, communicating with USB serial devices is supported through ttyUSBx
//! and ttyACMx.
//!
//! ## UART peripherals
//!
//! On earlier Pi models without Bluetooth, UART0 is used as a Linux serial console
//! if that feature is enabled. On more recent models with Bluetooth (3B, 3B+, Zero W), UART0
//! is connected to the Bluetooth module, and UART1 is used as a serial console if enabled.
//! Due to the limitations of UART1, in most cases you'll want to use UART0 for serial
//! communication. More details on the differences between UART0 and UART1 can be found
//! in the official Raspberry Pi [documentation].
//!
//! To disable the serial console, either deactivate it through `sudo raspi-config`, or
//! remove the line `enable_uart=1` from `/boot/config.txt`. You'll also want to remove
//! the parameter `console=serial0,115200` from `/boot/cmdline.txt`.
//!
//! On Pi models with Bluetooth, an extra step is required to either disable Bluetooth so
//! UART0 becomes available for serial communication, or tie UART1 to the Bluetooth module
//! instead of UART0.
//!
//! To disable Bluetooth, add 'dtoverlay=pi3-disable-bt' to `/boot/config.txt`. You'll also
//! need to disable the service that initializes the modem with `sudo systemctl disable hciuart`.
//!
//! To move the Bluetooth module to UART1, instead of the above-mentioned steps, add
//! `dtoverlay=pi3-miniuart-bt` to `/boot/config.txt`. You'll also need to edit `/lib/systemd/system/hciuart.service`
//! and replace `ttyAMA0` with `ttyS0`, and set a fixed core frequency by adding `core_freq=250` to
//! `/boot/config.txt`.
//!
//! By default, TX (outgoing data) for both UARTs is configured as BCM GPIO 14 (physical pin 8). RX (incoming data) for
//! both UARTs is configured as BCM GPIO 15 (physical pin 10). You can move these to different pins using the `uart0`
//! and `uart1` overlays, however none of the other pin options are exposed through the GPIO header on any of the
//! current Raspberry Pi models. They are only available through the Compute Module and Compute Module 3's SO-DIMM pads.
//!
//! Remember to reboot the Raspberry Pi after making any changes. More information on `enable_uart`, `pi3-disable-bt`,
//! `pi3-miniuart-bt`, `uart0` and `uart1` can be found in `/boot/overlays/README`.
//!
//! ### UART0 (`/dev/ttyAMA0`)
//!
//! PL011
//!
//! * TX: BCM GPIO 14 Alt0 (physical pin 8)
//! * RX: BCM GPIO 15 Alt0 (physical pin 10)
//! * CTS: BCM GPIO 16 Alt3 (physical pin 36)
//! * RTS: BCM GPIO 17 Alt3 (physical pin 11)
//!
//! ### UART1 (`/dev/ttyS0`)
//!
//! Mini UART
//!
//! * TX: BCM GPIO 14 Alt5 (physical pin 8)
//! * RX: BCM GPIO 15 Alt5 (physical pin 10)
//! * CTS: BCM GPIO 16 Alt5 (physical pin 36)
//! * RTS: BCM GPIO 17 Alt5 (physical pin 11)
//!
//! ## USB serial devices
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! [documentation]: https://www.raspberrypi.org/documentation/configuration/uart.md

use std::error;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::result;

use libc::{O_NDELAY, O_NOCTTY, O_NONBLOCK};

mod termios;

/// Errors that can occur when accessing the UART peripheral.
#[derive(Debug)]
pub enum Error {
    /// I/O error.
    Io(io::Error),
    /// Invalid value.
    InvalidValue,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "I/O error: {}", err),
            Error::InvalidValue => write!(f, "Invalid value"),
        }
    }
}

impl error::Error for Error {}

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

/// Result type returned from methods that can have `uart::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Serial devices.
///
/// The BCM283x SoC includes two UARTs. `Uart0` is the primary (PL011)
/// UART, which offers a full set of features. `Uart1` is an auxiliary
/// peripheral that's referred to as mini UART, with limited capabilities.
/// More information on the differences between the two UARTs, and how to
/// enable them, can be found [here].
///
/// In addition to the built-in UARTs, `Uart` also supports USB devices
/// with a serial interface. Depending on the type of device, these
/// can be accessed either through `/dev/ttyUSBx` or `/dev/ttyACMx`, where `x`
/// is an index starting at `0`. The numbering is based on the order
/// in which the devices are discovered by the kernel, so you'll need to find
/// a way to uniquely identify them when you have multiple devices connected
/// at the same time. For instance, you can find the assigned tty device name
/// based on the device id in `/dev/serial/by-id`.
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Device {
    Uart0,
    Uart1,
    Acm(u8),
    Usb(u8),
}

/// Parity modes.
///
/// `None` omits the parity bit. `Even` and `Odd` count the total number of
/// 1-bits in the data bits. `Mark` and `Space` always set the parity
/// bit to `1` or `0` respectively.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Parity {
    None,
    Even,
    Odd,
    Mark,
    Space,
}

/// Provides access to the Raspberry Pi's UART peripherals, and USB serial devices.
#[derive(Debug)]
pub struct Uart {
    device: File,
}

impl Uart {
    /// Constructs a new `Uart`.
    pub fn new(
        device: Device,
        line_speed: u32,
        parity: Parity,
        data_bits: u8,
        stop_bits: u8,
    ) -> Result<Uart> {
        let device = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_NOCTTY | O_NDELAY | O_NONBLOCK)
            .open(match device {
                Device::Uart0 => "/dev/ttyAMA0".to_owned(),
                Device::Uart1 => "/dev/ttyS0".to_owned(),
                Device::Acm(idx) => format!("/dev/ttyACM{}", idx),
                Device::Usb(idx) => format!("/dev/ttyUSB{}", idx),
            })?;

        termios::set_raw_mode(device.as_raw_fd())?;
        termios::ignore_carrier_detect(device.as_raw_fd())?;
        termios::enable_read(device.as_raw_fd())?;
        termios::set_line_speed(device.as_raw_fd(), line_speed)?;
        termios::set_parity(device.as_raw_fd(), parity)?;
        termios::set_data_bits(device.as_raw_fd(), data_bits)?;
        termios::set_stop_bits(device.as_raw_fd(), stop_bits)?;

        Ok(Uart { device })
    }

    /// Gets the line speed in baud (Bd).
    pub fn line_speed(&self) -> Result<u32> {
        Ok(termios::line_speed(self.device.as_raw_fd())?)
    }

    /// Sets the line speed in baud (Bd).
    ///
    /// Valid values are
    /// 0, 50, 75, 110, 134, 150, 200, 300, 600, 1_200, 1_800, 2_400, 4_800,
    /// 9_600, 19_200, 38_400, 57_600, 115_200, 230_400, 460_800, 500_000,
    /// 576_000, 921_600, 1_000_000, 1_152_000, 1_500_000, 2_000_000,
    /// 2_500_000, 3_000_000, 3_500_000 and 4_000_000,
    /// but support is device-dependent.
    pub fn set_line_speed(&self, line_speed: u32) -> Result<()> {
        termios::set_line_speed(self.device.as_raw_fd(), line_speed)?;

        Ok(())
    }

    /// Gets the parity bit.
    pub fn parity(&self) -> Result<Parity> {
        Ok(termios::parity(self.device.as_raw_fd())?)
    }

    /// Sets the parity bit.
    ///
    /// Support for parity is device-dependent.
    pub fn set_parity(&self, parity: Parity) -> Result<()> {
        termios::set_parity(self.device.as_raw_fd(), parity)?;

        Ok(())
    }

    /// Gets the number of data bits.
    pub fn data_bits(&self) -> Result<u8> {
        Ok(termios::data_bits(self.device.as_raw_fd())?)
    }

    /// Sets the number of data bits.
    ///
    /// Valid values are 5, 6, 7 or 8, but support is device-dependent.
    pub fn set_data_bits(&self, data_bits: u8) -> Result<()> {
        termios::set_data_bits(self.device.as_raw_fd(), data_bits)?;

        Ok(())
    }

    /// Gets the number of stop bits.
    pub fn stop_bits(&self) -> Result<u8> {
        Ok(termios::stop_bits(self.device.as_raw_fd())?)
    }

    /// Sets the number of stop bits.
    ///
    /// Valid values are 1 or 2, but support is device-dependent.
    pub fn set_stop_bits(&self, stop_bits: u8) -> Result<()> {
        termios::set_stop_bits(self.device.as_raw_fd(), stop_bits)?;

        Ok(())
    }

    /// Returns the status of the RTS/CTS hardware flow control setting.
    pub fn hardware_flow_control(&self) -> Result<bool> {
        unimplemented!()
    }

    /// Enables or disables RTS/CTS hardware flow control.
    ///
    /// Enabling flow control will configure the corresponding GPIO pins.
    /// More information on the GPIO pin numbers associated with RTS and
    /// CTS can be found [here].
    ///
    /// Support for RTS/CTS is device-dependent.
    ///
    /// [here]: index.html
    pub fn set_hardware_flow_control(&self, _enabled: bool) -> Result<()> {
        unimplemented!()
    }

    /// Receives incoming data from the device and stores it in `buffer`.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        match self.device.read(buffer) {
            Ok(bytes_read) => Ok(bytes_read),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
            Err(e) => Err(Error::Io(e)),
        }
    }

    /// Sends the contents of `buffer` to the device.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        match self.device.write(buffer) {
            Ok(bytes_written) => Ok(bytes_written),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
            Err(e) => Err(Error::Io(e)),
        }
    }

    /// .
    pub fn flush(&self) {
        unimplemented!()
    }

    /// .
    pub fn drain(&self) {
        unimplemented!()
    }

    // TODO: Enable available error detection (parity?) through termios
}
<commit_msg>Add with_path constructor<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the UART peripherals and USB serial devices.
//!
//! RPPAL controls the Raspberry Pi's main and auxiliary UART peripherals
//! through the `ttyAMA0` and `ttyS0` device interfaces. In addition to the built-in
//! UARTs, communicating with USB serial devices is supported through `ttyUSBx`
//! and `ttyACMx`.
//!
//! ## UART peripherals
//!
//! On earlier Pi models without Bluetooth, UART0 is used as a Linux serial console
//! if that feature is enabled. On more recent models with Bluetooth (3B, 3B+, Zero W), UART0
//! is connected to the Bluetooth module, and UART1 is used as a serial console if enabled.
//! Due to the limitations of UART1, in most cases you'll want to use UART0 for serial
//! communication. More details on the differences between UART0 and UART1 can be found
//! in the official Raspberry Pi [documentation].
//!
//! To disable the serial console, either deactivate it through `sudo raspi-config`, or
//! remove the line `enable_uart=1` from `/boot/config.txt`. You'll also want to remove
//! the parameter `console=serial0,115200` from `/boot/cmdline.txt`.
//!
//! On Pi models with Bluetooth, an extra step is required to either disable Bluetooth so
//! UART0 becomes available for serial communication, or tie UART1 to the Bluetooth module
//! instead of UART0.
//!
//! To disable Bluetooth, add 'dtoverlay=pi3-disable-bt' to `/boot/config.txt`. You'll also
//! need to disable the service that initializes the modem with `sudo systemctl disable hciuart`.
//!
//! To move the Bluetooth module to UART1, instead of the above-mentioned steps, add
//! `dtoverlay=pi3-miniuart-bt` to `/boot/config.txt`. You'll also need to edit `/lib/systemd/system/hciuart.service`
//! and replace `ttyAMA0` with `ttyS0`, and set a fixed core frequency by adding `core_freq=250` to
//! `/boot/config.txt`.
//!
//! By default, TX (outgoing data) for both UARTs is configured as BCM GPIO 14 (physical pin 8). RX (incoming data) for
//! both UARTs is configured as BCM GPIO 15 (physical pin 10). You can move these to different pins using the `uart0`
//! and `uart1` overlays, however none of the other pin options are exposed through the GPIO header on any of the
//! current Raspberry Pi models. They are only available through the Compute Module's SO-DIMM pads.
//!
//! Remember to reboot the Raspberry Pi after making any changes. More information on `enable_uart`, `pi3-disable-bt`,
//! `pi3-miniuart-bt`, `uart0` and `uart1` can be found in `/boot/overlays/README`.
//!
//! ### UART0 (`/dev/ttyAMA0`)
//!
//! PL011
//!
//! * TX: BCM GPIO 14 Alt0 (physical pin 8)
//! * RX: BCM GPIO 15 Alt0 (physical pin 10)
//! * CTS: BCM GPIO 16 Alt3 (physical pin 36)
//! * RTS: BCM GPIO 17 Alt3 (physical pin 11)
//!
//! ### UART1 (`/dev/ttyS0`)
//!
//! Mini UART
//!
//! * TX: BCM GPIO 14 Alt5 (physical pin 8)
//! * RX: BCM GPIO 15 Alt5 (physical pin 10)
//! * CTS: BCM GPIO 16 Alt5 (physical pin 36)
//! * RTS: BCM GPIO 17 Alt5 (physical pin 11)
//!
//! ## USB serial devices
//!
//! ## Troubleshooting
//!
//! ### Permission denied
//!
//! [documentation]: https://www.raspberrypi.org/documentation/configuration/uart.md

use std::error;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::result;

use libc::{O_NDELAY, O_NOCTTY, O_NONBLOCK};

mod termios;

/// Errors that can occur when accessing the UART peripheral.
#[derive(Debug)]
pub enum Error {
    /// I/O error.
    Io(io::Error),
    /// Invalid value.
    InvalidValue,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "I/O error: {}", err),
            Error::InvalidValue => write!(f, "Invalid value"),
        }
    }
}

impl error::Error for Error {}

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

/// Result type returned from methods that can have `uart::Error`s.
pub type Result<T> = result::Result<T, Error>;

/// Serial devices.
///
/// The BCM283x SoC includes two UARTs. `Uart0` is the primary (PL011)
/// UART, which offers a full set of features. `Uart1` is an auxiliary
/// peripheral that's referred to as mini UART, with limited capabilities.
/// More information on the differences between the two UARTs, and how to
/// enable them, can be found [here].
///
/// In addition to the built-in UARTs, `Uart` also supports USB devices
/// with a serial interface. Depending on the type of device, these
/// can be accessed either through `/dev/ttyUSBx` or `/dev/ttyACMx`, where `x`
/// is an index starting at `0`. The numbering is based on the order
/// in which the devices are discovered by the kernel, so you'll need to find
/// a way to uniquely identify them when you have multiple devices connected
/// at the same time. For instance, you can find the assigned tty device name
/// based on the device id in `/dev/serial/by-id`.
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Device {
    Uart0,
    Uart1,
    Acm(u8),
    Usb(u8),
}

/// Parity modes.
///
/// `None` omits the parity bit. `Even` and `Odd` count the total number of
/// 1-bits in the data bits. `Mark` and `Space` always set the parity
/// bit to `1` or `0` respectively.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Parity {
    None,
    Even,
    Odd,
    Mark,
    Space,
}

/// Provides access to the Raspberry Pi's UART peripherals and any USB serial devices.
#[derive(Debug)]
pub struct Uart {
    device: File,
}

impl Uart {
    /// Constructs a new `Uart`.
    pub fn new(
        device: Device,
        line_speed: u32,
        parity: Parity,
        data_bits: u8,
        stop_bits: u8,
    ) -> Result<Uart> {
        Self::with_path(
            match device {
                Device::Uart0 => "/dev/ttyAMA0".to_owned(),
                Device::Uart1 => "/dev/ttyS0".to_owned(),
                Device::Acm(idx) => format!("/dev/ttyACM{}", idx),
                Device::Usb(idx) => format!("/dev/ttyUSB{}", idx),
            },
            line_speed,
            parity,
            data_bits,
            stop_bits,
        )
    }

    /// Constructs a new `Uart` connected to the serial device specified by `path`.
    pub fn with_path<P: AsRef<Path>>(
        path: P,
        line_speed: u32,
        parity: Parity,
        data_bits: u8,
        stop_bits: u8,
    ) -> Result<Uart> {
        let device = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(O_NOCTTY | O_NDELAY | O_NONBLOCK)
            .open(path)?;

        // Enables character input mode, disables echoing and any special processing,
        // sets read() to non-blocking (VMIN) and timeout to 0 (VTIME).
        termios::set_raw_mode(device.as_raw_fd())?;

        // Ignore modem control lines (CLOCAL)
        termios::ignore_carrier_detect(device.as_raw_fd())?;

        // Enable receiver (CREAD)
        termios::enable_read(device.as_raw_fd())?;

        termios::set_line_speed(device.as_raw_fd(), line_speed)?;
        termios::set_parity(device.as_raw_fd(), parity)?;
        termios::set_data_bits(device.as_raw_fd(), data_bits)?;
        termios::set_stop_bits(device.as_raw_fd(), stop_bits)?;

        Ok(Uart { device })
    }

    /// Gets the line speed in baud (Bd).
    pub fn line_speed(&self) -> Result<u32> {
        termios::line_speed(self.device.as_raw_fd())
    }

    /// Sets the line speed in baud (Bd).
    ///
    /// Valid values are
    /// 0, 50, 75, 110, 134, 150, 200, 300, 600, 1_200, 1_800, 2_400, 4_800,
    /// 9_600, 19_200, 38_400, 57_600, 115_200, 230_400, 460_800, 500_000,
    /// 576_000, 921_600, 1_000_000, 1_152_000, 1_500_000, 2_000_000,
    /// 2_500_000, 3_000_000, 3_500_000 and 4_000_000,
    /// but support is device-dependent.
    pub fn set_line_speed(&self, line_speed: u32) -> Result<()> {
        termios::set_line_speed(self.device.as_raw_fd(), line_speed)
    }

    /// Gets the parity bit.
    pub fn parity(&self) -> Result<Parity> {
        termios::parity(self.device.as_raw_fd())
    }

    /// Sets the parity bit.
    ///
    /// Support for parity is device-dependent.
    pub fn set_parity(&self, parity: Parity) -> Result<()> {
        termios::set_parity(self.device.as_raw_fd(), parity)
    }

    /// Gets the number of data bits.
    pub fn data_bits(&self) -> Result<u8> {
        termios::data_bits(self.device.as_raw_fd())
    }

    /// Sets the number of data bits.
    ///
    /// Valid values are 5, 6, 7 or 8, but support is device-dependent.
    pub fn set_data_bits(&self, data_bits: u8) -> Result<()> {
        termios::set_data_bits(self.device.as_raw_fd(), data_bits)
    }

    /// Gets the number of stop bits.
    pub fn stop_bits(&self) -> Result<u8> {
        termios::stop_bits(self.device.as_raw_fd())
    }

    /// Sets the number of stop bits.
    ///
    /// Valid values are 1 or 2, but support is device-dependent.
    pub fn set_stop_bits(&self, stop_bits: u8) -> Result<()> {
        termios::set_stop_bits(self.device.as_raw_fd(), stop_bits)
    }

    /// Returns the status of the RTS/CTS hardware flow control setting.
    pub fn hardware_flow_control(&self) -> Result<bool> {
        unimplemented!()
    }

    /// Enables or disables RTS/CTS hardware flow control.
    ///
    /// Enabling flow control will configure the corresponding GPIO pins.
    /// More information on the GPIO pin numbers associated with RTS and
    /// CTS can be found [here].
    ///
    /// Support for RTS/CTS is device-dependent.
    ///
    /// [here]: index.html
    pub fn set_hardware_flow_control(&self, _enabled: bool) -> Result<()> {
        unimplemented!()
    }

    /// Receives incoming data from the device and stores it in `buffer`.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        match self.device.read(buffer) {
            Ok(bytes_read) => Ok(bytes_read),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
            Err(e) => Err(Error::Io(e)),
        }
    }

    /// Sends the contents of `buffer` to the device.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        match self.device.write(buffer) {
            Ok(bytes_written) => Ok(bytes_written),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
            Err(e) => Err(Error::Io(e)),
        }
    }

    /// .
    pub fn flush(&self) {
        unimplemented!()
    }

    /// .
    pub fn drain(&self) {
        unimplemented!()
    }

    // TODO: Enable available error detection (parity?) through termios
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
extern crate smallvec;

use std::rc::Rc;
use iup_sys::*;
use smallvec::SmallVec;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

impl CursorAttribute for CursorsCanvas {}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let cursors_canvas = Rc::new(CursorsCanvas::new());

    let radios_info = [
        ("None", Cursor::None),
        ("Arrow", Cursor::Arrow),
        ("Busy", Cursor::Busy),
        ("Cross", Cursor::Cross),
        ("Hand", Cursor::Hand),
        ("Help", Cursor::Help),
        ("Move", Cursor::Move),
        ("ResizeN", Cursor::ResizeN),
        ("ResizeS", Cursor::ResizeS),
        ("ResizeNS", Cursor::ResizeNS),
        ("ResizeW", Cursor::ResizeW),
        ("ResizeE", Cursor::ResizeE),
        ("ResizeWE", Cursor::ResizeWE),
        ("ResizeNE", Cursor::ResizeNE),
        ("ResizeSW", Cursor::ResizeSW),
        ("ResizeNW", Cursor::ResizeNW),
        ("ResizeSE", Cursor::ResizeSE),
        ("Text", Cursor::Text),
    ];

    let mut radios = SmallVec::<[Toggle; 32]>::new();
    for &(text, cur) in radios_info.iter() {
        let toggle = Toggle::new();
        toggle.set_title(text);
        let cursors_canvas2 = cursors_canvas.clone();
        toggle.action_event().add(move |checked| {
            if checked { cursors_canvas2.set_cursor(cur); }
        });
        radios.push(toggle);
    }
    // The arrow cursor is the default.
    radios[1].set_on(true);

    let grid = grid_box!(
        &radios[0],
        &radios[1],
        &radios[2],
        &radios[3],
        &radios[4],
        &radios[5],
        &radios[6],
        &radios[7],
        &radios[8],
        &radios[9],
        &radios[10],
        &radios[11],
        &radios[12],
        &radios[13],
        &radios[14],
        &radios[15],
        &radios[16],
        &radios[17],
    );
    grid.set_num_div(NumDiv::Fixed(2));
    grid.fit_all_to_children();
    let page = vbox!(
        &cursors_canvas,
        hbox!(
            fill!(),
            Radio::with_child(&grid),
            fill!(),
        ),
    );
    Box::new(page)
}

fn create_file_dialog_page() -> Box<Control> {

    let type_check_box = Toggle::new();
    type_check_box.set_title("Dialog Type:");

    let open_radio = Toggle::new();
    open_radio.set_title("Open");
    let save_radio = Toggle::new();
    save_radio.set_title("Save");
    let dir_radio = Toggle::new();
    dir_radio.set_title("Directory");
    let type_radio = Radio::with_child(&vbox!(
        &open_radio,
        &save_radio,
        &dir_radio,
    ));

    let dir_check_box = Toggle::new();
    dir_check_box.set_title("Directory:");

    let dir_text_box = Text::new();

    let show_dialog = Button::with_title("Show Dialog");
    let type_check_box_capt = type_check_box.clone();
    let open_radio_capt = open_radio.clone();
    let save_radio_capt = save_radio.clone();
    let dir_check_box_capt = dir_check_box.clone();
    let dir_text_box_capt = dir_text_box.clone();
    show_dialog.action_event().add(move || {
        let dialog = FileDlg::new();
        if type_check_box_capt.is_on() {
            dialog.set_dialog_type(if open_radio_capt.is_on() {
                FileDialogType::Open
            } else if save_radio_capt.is_on() {
                FileDialogType::Save
            } else {
                FileDialogType::Dir
            })
        }
        if dir_check_box_capt.is_on() {
            dialog.set_directory(&dir_text_box_capt.value());
        }
        dialog.popup(ScreenPosition::CenterParent, ScreenPosition::CenterParent)
              .expect("couldn't show file dialog");
    });

    let grid = grid_box!(
        type_check_box, type_radio,
        dir_check_box, dir_text_box,
        fill!(), show_dialog,
    );
    grid.set_alignment_lin_all(VAlignment::Top);
    grid.set_num_div(NumDiv::Fixed(2));
    grid.fit_all_to_children();
    Box::new(grid)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&*create_file_dialog_page()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<commit_msg>Allow turning on multiple file selection for file dialogs in Showcase<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
extern crate smallvec;

use std::rc::Rc;
use iup_sys::*;
use smallvec::SmallVec;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

impl CursorAttribute for CursorsCanvas {}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let cursors_canvas = Rc::new(CursorsCanvas::new());

    let radios_info = [
        ("None", Cursor::None),
        ("Arrow", Cursor::Arrow),
        ("Busy", Cursor::Busy),
        ("Cross", Cursor::Cross),
        ("Hand", Cursor::Hand),
        ("Help", Cursor::Help),
        ("Move", Cursor::Move),
        ("ResizeN", Cursor::ResizeN),
        ("ResizeS", Cursor::ResizeS),
        ("ResizeNS", Cursor::ResizeNS),
        ("ResizeW", Cursor::ResizeW),
        ("ResizeE", Cursor::ResizeE),
        ("ResizeWE", Cursor::ResizeWE),
        ("ResizeNE", Cursor::ResizeNE),
        ("ResizeSW", Cursor::ResizeSW),
        ("ResizeNW", Cursor::ResizeNW),
        ("ResizeSE", Cursor::ResizeSE),
        ("Text", Cursor::Text),
    ];

    let mut radios = SmallVec::<[Toggle; 32]>::new();
    for &(text, cur) in radios_info.iter() {
        let toggle = Toggle::new();
        toggle.set_title(text);
        let cursors_canvas2 = cursors_canvas.clone();
        toggle.action_event().add(move |checked| {
            if checked { cursors_canvas2.set_cursor(cur); }
        });
        radios.push(toggle);
    }
    // The arrow cursor is the default.
    radios[1].set_on(true);

    let grid = grid_box!(
        &radios[0],
        &radios[1],
        &radios[2],
        &radios[3],
        &radios[4],
        &radios[5],
        &radios[6],
        &radios[7],
        &radios[8],
        &radios[9],
        &radios[10],
        &radios[11],
        &radios[12],
        &radios[13],
        &radios[14],
        &radios[15],
        &radios[16],
        &radios[17],
    );
    grid.set_num_div(NumDiv::Fixed(2));
    grid.fit_all_to_children();
    let page = vbox!(
        &cursors_canvas,
        hbox!(
            fill!(),
            Radio::with_child(&grid),
            fill!(),
        ),
    );
    Box::new(page)
}

fn create_file_dialog_page() -> Box<Control> {
    let type_check_box = Toggle::new();
    type_check_box.set_title("Dialog Type:");

    let open_radio = Toggle::new();
    open_radio.set_title("Open");
    let save_radio = Toggle::new();
    save_radio.set_title("Save");
    let dir_radio = Toggle::new();
    dir_radio.set_title("Directory");
    let type_radio = Radio::with_child(&vbox!(
        &open_radio,
        &save_radio,
        &dir_radio,
    ));

    let dir_check_box = Toggle::new();
    dir_check_box.set_title("Directory:");

    let dir_text_box = Text::new();

    let multiple_files_check_box = Toggle::new();
    multiple_files_check_box.set_title("Multiple Files");

    let show_dialog = Button::with_title("Show Dialog");

    let type_check_box_capt = type_check_box.clone();
    let open_radio_capt = open_radio.clone();
    let save_radio_capt = save_radio.clone();
    let dir_check_box_capt = dir_check_box.clone();
    let dir_text_box_capt = dir_text_box.clone();
    let multiple_files_check_box_capt = multiple_files_check_box.clone();
    show_dialog.action_event().add(move || {
        let dialog = FileDlg::new();
        if type_check_box_capt.is_on() {
            dialog.set_dialog_type(if open_radio_capt.is_on() {
                FileDialogType::Open
            } else if save_radio_capt.is_on() {
                FileDialogType::Save
            } else {
                FileDialogType::Dir
            })
        }
        if dir_check_box_capt.is_on() {
            dialog.set_directory(&dir_text_box_capt.value());
        }
        if multiple_files_check_box_capt.is_on() {
            dialog.set_multiple_files(true);
        }
        dialog.popup(ScreenPosition::CenterParent, ScreenPosition::CenterParent)
              .expect("couldn't show file dialog");
    });

    let grid = grid_box!(
        type_check_box, type_radio,
        dir_check_box, dir_text_box,
        multiple_files_check_box, fill!(),
        fill!(), show_dialog,
    );
    grid.set_alignment_lin_all(VAlignment::Top);
    grid.set_num_div(NumDiv::Fixed(2));
    grid.fit_all_to_children();
    Box::new(grid)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&*create_file_dialog_page()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<|endoftext|>