<commit_before>#![deny(warnings)]

extern crate orbtk;

use orbtk::{Button, Placeable, Point, Rect, TextBox, Window};
use orbtk::callback::{Click, Enter};

#[derive(Debug, Clone)]
pub enum Token {
    Plus,
    Minus,
    Divide,
    Multiply,
    Exponent,
    OpenParen,
    CloseParen,
    Number(String),
}

impl Token {
    pub fn to_str(&self) -> &'static str {
        match self {
            &Token::Plus       => "Plus",
            &Token::Minus      => "Minus",
            &Token::Divide     => "Divide",
            &Token::Multiply   => "Multiply",
            &Token::Exponent   => "Exponent",
            &Token::OpenParen  => "OpenParen",
            &Token::CloseParen => "CloseParen",
            &Token::Number(_)  => "Number",
        }
    }

    pub fn to_string(&self) -> String {
        self.to_str().to_owned()
    }
}

#[derive(Debug,  Clone)]
pub enum ParseError {
    InvalidNumber(String),
    UnrecognizedToken(String),
    UnexpectedToken(String, &'static str),
    UnexpectedEndOfInput,
    OtherError(String),
}

#[derive(Clone,Debug)]
pub struct IntermediateResult {
    value: f64,
    tokens_read: usize,
}

impl IntermediateResult {
    fn new(value: f64, tokens_read: usize) -> Self {
        IntermediateResult {
            value: value,
            tokens_read: tokens_read,
        }
    }
}

pub trait OperatorFunctions {
    fn is_operator(self) -> bool;
    fn operator_type(self) -> Token;
}

impl OperatorFunctions for char {
    fn is_operator(self) -> bool {
        self == '+' ||
        self == '-' ||
        self == '*' ||
        self == '/' ||
        self == '^' ||
        self == '(' ||
        self == ')'
    }

    fn operator_type(self) -> Token {
        match self {
            '+' => Token::Plus,
            '-' => Token::Minus,
            '/' => Token::Divide,
            '*' => Token::Multiply,
            '^' => Token::Exponent,
            '(' => Token::OpenParen,
            ')' => Token::CloseParen,
            _   => panic!("Invalid operator")
        }
    }
}

pub fn tokenize(input: &str) -> Result<Vec<Token>, ParseError> {
    // CONSIDER: input.len() too generous?
    let mut tokens = Vec::with_capacity(input.len());

    // TODO: Not this. Modify to use iterator
    let chars: Vec<char> = input.chars().collect();

    let input_length = chars.len();
    let mut current_pos = 0;
    while current_pos < input_length {
        let c = chars[current_pos];
        if c.is_digit(10) {
            let token_string = consume_number(&chars[current_pos..]);
            current_pos += token_string.len();
            tokens.push(Token::Number(token_string));
        } else if c.is_operator() {
            tokens.push(c.operator_type());
            current_pos += 1;
        } else if c.is_whitespace() {
            current_pos += 1;
        } else {
            let token_string = consume_until_new_token(&chars[current_pos..]);
            return Err(ParseError::UnrecognizedToken(token_string));
        }
    }
    Ok(tokens)
}

fn consume_number(input: &[char]) -> String {
    // CONSIDER: input.len seems a bit generous
    let mut number = String::with_capacity(input.len());
    let mut has_decimal_point = false;
    for &c in input {
        if c == '.' {
            if has_decimal_point {
                break;
            } else {
                number.push(c);
                has_decimal_point = true;
            }
        } else if c.is_digit(10) {
            number.push(c);
        } else {
            break;
        }
    }
    number
}

fn consume_until_new_token(input: &[char]) -> String {
    input.iter()
         .take_while(|c| !(c.is_whitespace() || c.is_operator() || c.is_digit(10)))
         .map(|&c| c)
         .collect()
}

// Addition and subtraction
pub fn e_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut t1 = try!(t_expr(token_list));
    let mut index = t1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Plus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value += t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Minus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value -= t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        };
        index = t1.tokens_read;
    }
    Ok(t1)
}

// Multiplication and division
pub fn t_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut f1 = try!(f_expr(token_list));
    let mut index = f1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Multiply => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                f1.value *= f2.value;
                f1.tokens_read += f2.tokens_read + 1;
            }
            Token::Divide => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                if f2.value == 0.0 {
                    return Err(ParseError::OtherError("Divide by zero error".to_owned()));
                } else {
                    f1.value /= f2.value;
                    f1.tokens_read += f2.tokens_read + 1;
                }
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = f1.tokens_read;
    }
    Ok(f1)
}

// Exponentiation
pub fn f_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut g1 = try!(g_expr(token_list));
    let mut index = g1.tokens_read;
    let token_len = token_list.len();
    while index < token_len {
        match token_list[index] {
            Token::Exponent => {
                let f = try!(f_expr(&token_list[index+1..]));
                g1.value = g1.value.powf(f.value);
                g1.tokens_read += f.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = g1.tokens_read;
    }
    Ok(g1)
}

// Numbers and parenthesized expressions
pub fn g_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    if !token_list.is_empty() {
        match token_list[0] {
            Token::Number(ref n) => {
                n.parse::<f64>()
                 .map_err(|_| ParseError::InvalidNumber(n.clone()))
                 .and_then(|num| Ok(IntermediateResult::new(num, 1)))
            }
            Token::Minus => {
                if token_list.len() > 1 {
                    if let Token::Number(ref n) = token_list[1] {
                        n.parse::<f64>()
                         .map_err(|_| ParseError::InvalidNumber(n.clone()))
                         .and_then(|num| Ok(IntermediateResult::new(-1.0 * num, 2)))
                    } else {
                        Err(ParseError::UnexpectedToken(token_list[1].to_string(), "number"))
                    }
                } else {
                    Err(ParseError::UnexpectedEndOfInput)
                }
            }
            Token::OpenParen => {
                let expr = e_expr(&token_list[1..]);
                match expr {
                    Ok(ir) => {
                        let close_paren = ir.tokens_read + 1;
                        if close_paren < token_list.len() {
                            match token_list[close_paren] {
                                Token::CloseParen => Ok(IntermediateResult::new(ir.value, close_paren+1)),
                                _ => Err(ParseError::UnexpectedToken(token_list[close_paren].to_string(), ")")),
                            }
                        } else {
                            Err(ParseError::OtherError("no matching close parenthesis found.".to_owned()))
                        }
                    }
                    Err(e) => Err(e),
                }
            }
            _ => Err(ParseError::UnexpectedToken(token_list[0].to_string(), "number"))
        }
    } else {
        Err(ParseError::UnexpectedEndOfInput)
    }
}

pub fn parse(tokens: Vec<Token>) -> Result<String, ParseError> {
    e_expr(&tokens).map(|answer| answer.value.to_string())
}

fn eval(input: &str) -> String {
    match tokenize(input).and_then(parse) {
        Ok(s) => s,
        Err(_e) => "Syntax Error".to_string()
    }
}

fn main(){
    let window = Window::new(Rect::new(100, 100, 148, 200), "Calculator");

    {
        let text_box = TextBox::new()
            .position(0, 0)
            .size(300, 16)
            .on_enter(move |text_box: &TextBox| {
                let input = text_box.text.get();
                let result = eval(&input);
                text_box.text_i.set(result.len());
                text_box.text.set(result);
            })
            .place(&window);

        let mut col = 0;
        let mut row = 0;

        {
            let mut btn = |name| {
                let text_box_clone = text_box.clone();
                Button::new()
                    .position(col * 36 + 4, row * 36 + 20)
                    .size(32, 32)
                    .text(name)
                    .text_offset(12, 8)
                    .on_click(move |_button: &Button, _point: Point| {
                        let text_i = text_box_clone.text_i.get();

                        let text = text_box_clone.text.get();

                        let mut new_text = String::new();
                        new_text.push_str(&text[.. text_i]);
                        new_text.push_str(name);
                        new_text.push_str(&text[text_i ..]);

                        text_box_clone.text.set(new_text);

                        text_box_clone.text_i.set(text_i + name.len());
                    })
                    .place(&window);
                col += 1;
                if col >= 4 {
                    col = 0;
                    row += 1;
                }
            };

            btn("("); btn(")"); btn("^"); btn("/");
            btn("7"); btn("8"); btn("9"); btn("*");
            btn("4"); btn("5"); btn("6"); btn("-");
            btn("1"); btn("2"); btn("3"); btn("+");
            btn("0"); btn("0"); btn(".");
        }

        {
            let text_box_clone = text_box.clone();
            Button::new()
                .position(col * 36 + 4, row * 36 + 20)
                .size(32, 32)
                .text("=")
                .text_offset(12, 8)
                .on_click(move |_button: &Button, _point: Point| {
                    text_box_clone.emit_enter();
                })
                .place(&window);
        }
    }

    window.exec();
}
<commit_msg>Don't print syntax error on empty box<commit_after>#![deny(warnings)]

extern crate orbtk;

use orbtk::{Button, Placeable, Point, Rect, TextBox, Window};
use orbtk::callback::{Click, Enter};

#[derive(Debug, Clone)]
pub enum Token {
    Plus,
    Minus,
    Divide,
    Multiply,
    Exponent,
    OpenParen,
    CloseParen,
    Number(String),
}

impl Token {
    pub fn to_str(&self) -> &'static str {
        match self {
            &Token::Plus       => "Plus",
            &Token::Minus      => "Minus",
            &Token::Divide     => "Divide",
            &Token::Multiply   => "Multiply",
            &Token::Exponent   => "Exponent",
            &Token::OpenParen  => "OpenParen",
            &Token::CloseParen => "CloseParen",
            &Token::Number(_)  => "Number",
        }
    }

    pub fn to_string(&self) -> String {
        self.to_str().to_owned()
    }
}

#[derive(Debug,  Clone)]
pub enum ParseError {
    InvalidNumber(String),
    UnrecognizedToken(String),
    UnexpectedToken(String, &'static str),
    UnexpectedEndOfInput,
    OtherError(String),
}

#[derive(Clone,Debug)]
pub struct IntermediateResult {
    value: f64,
    tokens_read: usize,
}

impl IntermediateResult {
    fn new(value: f64, tokens_read: usize) -> Self {
        IntermediateResult {
            value: value,
            tokens_read: tokens_read,
        }
    }
}

pub trait OperatorFunctions {
    fn is_operator(self) -> bool;
    fn operator_type(self) -> Token;
}

impl OperatorFunctions for char {
    fn is_operator(self) -> bool {
        self == '+' ||
        self == '-' ||
        self == '*' ||
        self == '/' ||
        self == '^' ||
        self == '(' ||
        self == ')'
    }

    fn operator_type(self) -> Token {
        match self {
            '+' => Token::Plus,
            '-' => Token::Minus,
            '/' => Token::Divide,
            '*' => Token::Multiply,
            '^' => Token::Exponent,
            '(' => Token::OpenParen,
            ')' => Token::CloseParen,
            _   => panic!("Invalid operator")
        }
    }
}

pub fn tokenize(input: &str) -> Result<Vec<Token>, ParseError> {
    // CONSIDER: input.len() too generous?
    let mut tokens = Vec::with_capacity(input.len());

    // TODO: Not this. Modify to use iterator
    let chars: Vec<char> = input.chars().collect();

    let input_length = chars.len();
    let mut current_pos = 0;
    while current_pos < input_length {
        let c = chars[current_pos];
        if c.is_digit(10) {
            let token_string = consume_number(&chars[current_pos..]);
            current_pos += token_string.len();
            tokens.push(Token::Number(token_string));
        } else if c.is_operator() {
            tokens.push(c.operator_type());
            current_pos += 1;
        } else if c.is_whitespace() {
            current_pos += 1;
        } else {
            let token_string = consume_until_new_token(&chars[current_pos..]);
            return Err(ParseError::UnrecognizedToken(token_string));
        }
    }
    Ok(tokens)
}

fn consume_number(input: &[char]) -> String {
    // CONSIDER: input.len seems a bit generous
    let mut number = String::with_capacity(input.len());
    let mut has_decimal_point = false;
    for &c in input {
        if c == '.' {
            if has_decimal_point {
                break;
            } else {
                number.push(c);
                has_decimal_point = true;
            }
        } else if c.is_digit(10) {
            number.push(c);
        } else {
            break;
        }
    }
    number
}

fn consume_until_new_token(input: &[char]) -> String {
    input.iter()
         .take_while(|c| !(c.is_whitespace() || c.is_operator() || c.is_digit(10)))
         .map(|&c| c)
         .collect()
}

// Addition and subtraction
pub fn e_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut t1 = try!(t_expr(token_list));
    let mut index = t1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Plus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value += t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Minus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value -= t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        };
        index = t1.tokens_read;
    }
    Ok(t1)
}

// Multiplication and division
pub fn t_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut f1 = try!(f_expr(token_list));
    let mut index = f1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Multiply => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                f1.value *= f2.value;
                f1.tokens_read += f2.tokens_read + 1;
            }
            Token::Divide => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                if f2.value == 0.0 {
                    return Err(ParseError::OtherError("Divide by zero error".to_owned()));
                } else {
                    f1.value /= f2.value;
                    f1.tokens_read += f2.tokens_read + 1;
                }
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = f1.tokens_read;
    }
    Ok(f1)
}

// Exponentiation
pub fn f_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut g1 = try!(g_expr(token_list));
    let mut index = g1.tokens_read;
    let token_len = token_list.len();
    while index < token_len {
        match token_list[index] {
            Token::Exponent => {
                let f = try!(f_expr(&token_list[index+1..]));
                g1.value = g1.value.powf(f.value);
                g1.tokens_read += f.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = g1.tokens_read;
    }
    Ok(g1)
}

// Numbers and parenthesized expressions
pub fn g_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    if !token_list.is_empty() {
        match token_list[0] {
            Token::Number(ref n) => {
                n.parse::<f64>()
                 .map_err(|_| ParseError::InvalidNumber(n.clone()))
                 .and_then(|num| Ok(IntermediateResult::new(num, 1)))
            }
            Token::Minus => {
                if token_list.len() > 1 {
                    if let Token::Number(ref n) = token_list[1] {
                        n.parse::<f64>()
                         .map_err(|_| ParseError::InvalidNumber(n.clone()))
                         .and_then(|num| Ok(IntermediateResult::new(-1.0 * num, 2)))
                    } else {
                        Err(ParseError::UnexpectedToken(token_list[1].to_string(), "number"))
                    }
                } else {
                    Err(ParseError::UnexpectedEndOfInput)
                }
            }
            Token::OpenParen => {
                let expr = e_expr(&token_list[1..]);
                match expr {
                    Ok(ir) => {
                        let close_paren = ir.tokens_read + 1;
                        if close_paren < token_list.len() {
                            match token_list[close_paren] {
                                Token::CloseParen => Ok(IntermediateResult::new(ir.value, close_paren+1)),
                                _ => Err(ParseError::UnexpectedToken(token_list[close_paren].to_string(), ")")),
                            }
                        } else {
                            Err(ParseError::OtherError("no matching close parenthesis found.".to_owned()))
                        }
                    }
                    Err(e) => Err(e),
                }
            }
            _ => Err(ParseError::UnexpectedToken(token_list[0].to_string(), "number"))
        }
    } else {
        Err(ParseError::UnexpectedEndOfInput)
    }
}

pub fn parse(tokens: Vec<Token>) -> Result<String, ParseError> {
    e_expr(&tokens).map(|answer| answer.value.to_string())
}

fn eval(input: &str) -> String {
    match tokenize(input).and_then(parse) {
        Ok(s) => s,
        Err(_e) => "Syntax Error".to_string()
    }
}

fn main(){
    let window = Window::new(Rect::new(100, 100, 148, 200), "Calculator");

    {
        let text_box = TextBox::new()
            .position(0, 0)
            .size(300, 16)
            .on_enter(move |text_box: &TextBox| {
                let input = text_box.text.get();
                if ! input.is_empty() {
                    let result = eval(&input);
                    text_box.text_i.set(result.len());
                    text_box.text.set(result);
                }
            })
            .place(&window);

        let mut col = 0;
        let mut row = 0;

        {
            let mut btn = |name| {
                let text_box_clone = text_box.clone();
                Button::new()
                    .position(col * 36 + 4, row * 36 + 20)
                    .size(32, 32)
                    .text(name)
                    .text_offset(12, 8)
                    .on_click(move |_button: &Button, _point: Point| {
                        let text_i = text_box_clone.text_i.get();

                        let text = text_box_clone.text.get();

                        let mut new_text = String::new();
                        new_text.push_str(&text[.. text_i]);
                        new_text.push_str(name);
                        new_text.push_str(&text[text_i ..]);

                        text_box_clone.text.set(new_text);

                        text_box_clone.text_i.set(text_i + name.len());
                    })
                    .place(&window);
                col += 1;
                if col >= 4 {
                    col = 0;
                    row += 1;
                }
            };

            btn("("); btn(")"); btn("^"); btn("/");
            btn("7"); btn("8"); btn("9"); btn("*");
            btn("4"); btn("5"); btn("6"); btn("-");
            btn("1"); btn("2"); btn("3"); btn("+");
            btn("0"); btn("0"); btn(".");
        }

        {
            let text_box_clone = text_box.clone();
            Button::new()
                .position(col * 36 + 4, row * 36 + 20)
                .size(32, 32)
                .text("=")
                .text_offset(12, 8)
                .on_click(move |_button: &Button, _point: Point| {
                    text_box_clone.emit_enter();
                })
                .place(&window);
        }
    }

    window.exec();
}
<|endoftext|>


<commit_before>extern crate orbtk;

use orbtk::{Button, Point, Rect, TextBox, Window};
use orbtk::callback::{Click, Enter};
use orbtk::place::Place;

#[derive(Debug, Clone)]
pub enum Token {
    Plus,
    Minus,
    Divide,
    Multiply,
    Exponent,
    OpenParen,
    CloseParen,
    Number(String),
}

impl Token {
    pub fn to_str(&self) -> &'static str {
        match self {
            &Token::Plus       => "Plus",
            &Token::Minus      => "Minus",
            &Token::Divide     => "Divide",
            &Token::Multiply   => "Multiply",
            &Token::Exponent   => "Exponent",
            &Token::OpenParen  => "OpenParen",
            &Token::CloseParen => "CloseParen",
            &Token::Number(_)  => "Number",
        }
    }

    pub fn to_string(&self) -> String {
        self.to_str().to_owned()
    }
}

#[derive(Debug,  Clone)]
pub enum ParseError {
    InvalidNumber(String),
    UnrecognizedToken(String),
    UnexpectedToken(String, &'static str),
    UnexpectedEndOfInput,
    OtherError(String),
}

#[derive(Clone,Debug)]
pub struct IntermediateResult {
    value: f64,
    tokens_read: usize,
}

impl IntermediateResult {
    fn new(value: f64, tokens_read: usize) -> Self {
        IntermediateResult {
            value: value,
            tokens_read: tokens_read,
        }
    }
}

pub trait OperatorFunctions {
    fn is_operator(self) -> bool;
    fn operator_type(self) -> Token;
}

impl OperatorFunctions for char {
    fn is_operator(self) -> bool {
        self == '+' ||
        self == '-' ||
        self == '*' ||
        self == '/' ||
        self == '^' ||
        self == '(' ||
        self == ')'
    }

    fn operator_type(self) -> Token {
        match self {
            '+' => Token::Plus,
            '-' => Token::Minus,
            '/' => Token::Divide,
            '*' => Token::Multiply,
            '^' => Token::Exponent,
            '(' => Token::OpenParen,
            ')' => Token::CloseParen,
            _   => panic!("Invalid operator")
        }
    }
}

pub fn tokenize(input: &str) -> Result<Vec<Token>, ParseError> {
    // CONSIDER: input.len() too generous?
    let mut tokens = Vec::with_capacity(input.len());

    // TODO: Not this. Modify to use iterator
    let chars: Vec<char> = input.chars().collect();

    let input_length = chars.len();
    let mut current_pos = 0;
    while current_pos < input_length {
        let c = chars[current_pos];
        if c.is_digit(10) {
            let token_string = consume_number(&chars[current_pos..]);
            current_pos += token_string.len();
            tokens.push(Token::Number(token_string));
        } else if c.is_operator() {
            tokens.push(c.operator_type());
            current_pos += 1;
        } else if c.is_whitespace() {
            current_pos += 1;
        } else {
            let token_string = consume_until_new_token(&chars[current_pos..]);
            return Err(ParseError::UnrecognizedToken(token_string));
        }
    }
    Ok(tokens)
}

fn consume_number(input: &[char]) -> String {
    // CONSIDER: input.len seems a bit generous
    let mut number = String::with_capacity(input.len());
    let mut has_decimal_point = false;
    for &c in input {
        if c == '.' {
            if has_decimal_point {
                break;
            } else {
                number.push(c);
                has_decimal_point = true;
            }
        } else if c.is_digit(10) {
            number.push(c);
        } else {
            break;
        }
    }
    number
}

fn consume_until_new_token(input: &[char]) -> String {
    input.iter()
         .take_while(|c| !(c.is_whitespace() || c.is_operator() || c.is_digit(10)))
         .map(|&c| c)
         .collect()
}

// Addition and subtraction
pub fn e_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut t1 = try!(t_expr(token_list));
    let mut index = t1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Plus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value += t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Minus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value -= t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        };
        index = t1.tokens_read;
    }
    Ok(t1)
}

// Multiplication and division
pub fn t_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut f1 = try!(f_expr(token_list));
    let mut index = f1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Multiply => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                f1.value *= f2.value;
                f1.tokens_read += f2.tokens_read + 1;
            }
            Token::Divide => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                if f2.value == 0.0 {
                    return Err(ParseError::OtherError("Divide by zero error".to_owned()));
                } else {
                    f1.value /= f2.value;
                    f1.tokens_read += f2.tokens_read + 1;
                }
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = f1.tokens_read;
    }
    Ok(f1)
}

// Exponentiation
#[cfg(not(target_os = "redox"))]
pub fn f_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut g1 = try!(g_expr(token_list));
    let mut index = g1.tokens_read;
    let token_len = token_list.len();
    while index < token_len {
        match token_list[index] {
            Token::Exponent => {
                let f = try!(f_expr(&token_list[index+1..]));
                g1.value = g1.value.powf(f.value);
                g1.tokens_read += f.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = g1.tokens_read;
    }
    Ok(g1)
}

#[cfg(target_os = "redox")]
pub fn f_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut g1 = try!(g_expr(token_list));
    let mut index = g1.tokens_read;
    let token_len = token_list.len();
    while index < token_len {
        match token_list[index] {
            Token::Exponent => {
                let f = try!(f_expr(&token_list[index+1..]));
                //TODO: g1.value = g1.value.powf(f.value);
                g1.tokens_read += f.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = g1.tokens_read;
    }
    Ok(g1)
}

// Numbers and parenthesized expressions
pub fn g_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    if !token_list.is_empty() {
        match token_list[0] {
            Token::Number(ref n) => {
                n.parse::<f64>()
                 .map_err(|_| ParseError::InvalidNumber(n.clone()))
                 .and_then(|num| Ok(IntermediateResult::new(num, 1)))
            }
            Token::Minus => {
                if token_list.len() > 1 {
                    if let Token::Number(ref n) = token_list[1] {
                        n.parse::<f64>()
                         .map_err(|_| ParseError::InvalidNumber(n.clone()))
                         .and_then(|num| Ok(IntermediateResult::new(-1.0 * num, 2)))
                    } else {
                        Err(ParseError::UnexpectedToken(token_list[1].to_string(), "number"))
                    }
                } else {
                    Err(ParseError::UnexpectedEndOfInput)
                }
            }
            Token::OpenParen => {
                let expr = e_expr(&token_list[1..]);
                match expr {
                    Ok(ir) => {
                        let close_paren = ir.tokens_read + 1;
                        if close_paren < token_list.len() {
                            match token_list[close_paren] {
                                Token::CloseParen => Ok(IntermediateResult::new(ir.value, close_paren+1)),
                                _ => Err(ParseError::UnexpectedToken(token_list[close_paren].to_string(), ")")),
                            }
                        } else {
                            Err(ParseError::OtherError("no matching close parenthesis found.".to_owned()))
                        }
                    }
                    Err(e) => Err(e),
                }
            }
            _ => Err(ParseError::UnexpectedToken(token_list[0].to_string(), "number"))
        }
    } else {
        Err(ParseError::UnexpectedEndOfInput)
    }
}

pub fn parse(tokens: Vec<Token>) -> Result<String, ParseError> {
    e_expr(&tokens).map(|answer| answer.value.to_string())
}

fn eval(input: &str) -> String {
    match tokenize(input).and_then(parse) {
        Ok(s) => s,
        Err(_e) => "Syntax Error".to_string()
    }
}

fn main(){
    let mut window = Window::new(Rect::new(100, 100, 148, 200), "Calculator");

    {
        let text_box = TextBox::new()
            .position(0, 0)
            .size(300, 16)
            .on_enter(move |text_box: &TextBox| {
                let input = text_box.text.get();
                let result = eval(&input);
                text_box.text_i.set(result.len());
                text_box.text.set(result);
            })
            .place(&mut window);

        let mut col = 0;
        let mut row = 0;

        {
            let mut btn = |name| {
                let text_box_clone = text_box.clone();
                Button::new()
                    .position(col * 36 + 4, row * 36 + 20)
                    .size(32, 32)
                    .text(name)
                    .text_offset(12, 8)
                    .on_click(move |_button: &Button, _point: Point| {
                        let text_i = text_box_clone.text_i.get();

                        let text = text_box_clone.text.get();

                        let mut new_text = String::new();
                        new_text.push_str(&text[.. text_i]);
                        new_text.push_str(name);
                        new_text.push_str(&text[text_i ..]);

                        text_box_clone.text.set(new_text);

                        text_box_clone.text_i.set(text_i + name.len());
                    })
                    .place(&mut window);
                col += 1;
                if col >= 4 {
                    col = 0;
                    row += 1;
                }
            };

            btn("("); btn(")"); btn("^"); btn("/");
            btn("7"); btn("8"); btn("9"); btn("*");
            btn("4"); btn("5"); btn("6"); btn("-");
            btn("1"); btn("2"); btn("3"); btn("+");
            btn("0"); btn("0"); btn(".");
        }

        {
            let text_box_clone = text_box.clone();
            Button::new()
                .position(col * 36 + 4, row * 36 + 20)
                .size(32, 32)
                .text("=")
                .text_offset(12, 8)
                .on_click(move |_button: &Button, _point: Point| {
                    text_box_clone.emit_enter();
                })
                .place(&mut window);
        }
    }

    window.exec();
}
<commit_msg>Reenable exponents in calculator<commit_after>extern crate orbtk;

use orbtk::{Button, Point, Rect, TextBox, Window};
use orbtk::callback::{Click, Enter};
use orbtk::place::Place;

#[derive(Debug, Clone)]
pub enum Token {
    Plus,
    Minus,
    Divide,
    Multiply,
    Exponent,
    OpenParen,
    CloseParen,
    Number(String),
}

impl Token {
    pub fn to_str(&self) -> &'static str {
        match self {
            &Token::Plus       => "Plus",
            &Token::Minus      => "Minus",
            &Token::Divide     => "Divide",
            &Token::Multiply   => "Multiply",
            &Token::Exponent   => "Exponent",
            &Token::OpenParen  => "OpenParen",
            &Token::CloseParen => "CloseParen",
            &Token::Number(_)  => "Number",
        }
    }

    pub fn to_string(&self) -> String {
        self.to_str().to_owned()
    }
}

#[derive(Debug,  Clone)]
pub enum ParseError {
    InvalidNumber(String),
    UnrecognizedToken(String),
    UnexpectedToken(String, &'static str),
    UnexpectedEndOfInput,
    OtherError(String),
}

#[derive(Clone,Debug)]
pub struct IntermediateResult {
    value: f64,
    tokens_read: usize,
}

impl IntermediateResult {
    fn new(value: f64, tokens_read: usize) -> Self {
        IntermediateResult {
            value: value,
            tokens_read: tokens_read,
        }
    }
}

pub trait OperatorFunctions {
    fn is_operator(self) -> bool;
    fn operator_type(self) -> Token;
}

impl OperatorFunctions for char {
    fn is_operator(self) -> bool {
        self == '+' ||
        self == '-' ||
        self == '*' ||
        self == '/' ||
        self == '^' ||
        self == '(' ||
        self == ')'
    }

    fn operator_type(self) -> Token {
        match self {
            '+' => Token::Plus,
            '-' => Token::Minus,
            '/' => Token::Divide,
            '*' => Token::Multiply,
            '^' => Token::Exponent,
            '(' => Token::OpenParen,
            ')' => Token::CloseParen,
            _   => panic!("Invalid operator")
        }
    }
}

pub fn tokenize(input: &str) -> Result<Vec<Token>, ParseError> {
    // CONSIDER: input.len() too generous?
    let mut tokens = Vec::with_capacity(input.len());

    // TODO: Not this. Modify to use iterator
    let chars: Vec<char> = input.chars().collect();

    let input_length = chars.len();
    let mut current_pos = 0;
    while current_pos < input_length {
        let c = chars[current_pos];
        if c.is_digit(10) {
            let token_string = consume_number(&chars[current_pos..]);
            current_pos += token_string.len();
            tokens.push(Token::Number(token_string));
        } else if c.is_operator() {
            tokens.push(c.operator_type());
            current_pos += 1;
        } else if c.is_whitespace() {
            current_pos += 1;
        } else {
            let token_string = consume_until_new_token(&chars[current_pos..]);
            return Err(ParseError::UnrecognizedToken(token_string));
        }
    }
    Ok(tokens)
}

fn consume_number(input: &[char]) -> String {
    // CONSIDER: input.len seems a bit generous
    let mut number = String::with_capacity(input.len());
    let mut has_decimal_point = false;
    for &c in input {
        if c == '.' {
            if has_decimal_point {
                break;
            } else {
                number.push(c);
                has_decimal_point = true;
            }
        } else if c.is_digit(10) {
            number.push(c);
        } else {
            break;
        }
    }
    number
}

fn consume_until_new_token(input: &[char]) -> String {
    input.iter()
         .take_while(|c| !(c.is_whitespace() || c.is_operator() || c.is_digit(10)))
         .map(|&c| c)
         .collect()
}

// Addition and subtraction
pub fn e_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut t1 = try!(t_expr(token_list));
    let mut index = t1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Plus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value += t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Minus => {
                let t2 = try!(t_expr(&token_list[index+1..]));
                t1.value -= t2.value;
                t1.tokens_read += t2.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        };
        index = t1.tokens_read;
    }
    Ok(t1)
}

// Multiplication and division
pub fn t_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut f1 = try!(f_expr(token_list));
    let mut index = f1.tokens_read;

    while index < token_list.len() {
        match token_list[index] {
            Token::Multiply => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                f1.value *= f2.value;
                f1.tokens_read += f2.tokens_read + 1;
            }
            Token::Divide => {
                let f2 = try!(f_expr(&token_list[index+1..]));
                if f2.value == 0.0 {
                    return Err(ParseError::OtherError("Divide by zero error".to_owned()));
                } else {
                    f1.value /= f2.value;
                    f1.tokens_read += f2.tokens_read + 1;
                }
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = f1.tokens_read;
    }
    Ok(f1)
}

// Exponentiation
pub fn f_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    let mut g1 = try!(g_expr(token_list));
    let mut index = g1.tokens_read;
    let token_len = token_list.len();
    while index < token_len {
        match token_list[index] {
            Token::Exponent => {
                let f = try!(f_expr(&token_list[index+1..]));
                g1.value = g1.value.powf(f.value);
                g1.tokens_read += f.tokens_read + 1;
            }
            Token::Number(ref n) => return Err(ParseError::UnexpectedToken(n.clone(),"operator")),
            _ => break,
        }
        index = g1.tokens_read;
    }
    Ok(g1)
}

// Numbers and parenthesized expressions
pub fn g_expr(token_list: &[Token]) -> Result<IntermediateResult, ParseError> {
    if !token_list.is_empty() {
        match token_list[0] {
            Token::Number(ref n) => {
                n.parse::<f64>()
                 .map_err(|_| ParseError::InvalidNumber(n.clone()))
                 .and_then(|num| Ok(IntermediateResult::new(num, 1)))
            }
            Token::Minus => {
                if token_list.len() > 1 {
                    if let Token::Number(ref n) = token_list[1] {
                        n.parse::<f64>()
                         .map_err(|_| ParseError::InvalidNumber(n.clone()))
                         .and_then(|num| Ok(IntermediateResult::new(-1.0 * num, 2)))
                    } else {
                        Err(ParseError::UnexpectedToken(token_list[1].to_string(), "number"))
                    }
                } else {
                    Err(ParseError::UnexpectedEndOfInput)
                }
            }
            Token::OpenParen => {
                let expr = e_expr(&token_list[1..]);
                match expr {
                    Ok(ir) => {
                        let close_paren = ir.tokens_read + 1;
                        if close_paren < token_list.len() {
                            match token_list[close_paren] {
                                Token::CloseParen => Ok(IntermediateResult::new(ir.value, close_paren+1)),
                                _ => Err(ParseError::UnexpectedToken(token_list[close_paren].to_string(), ")")),
                            }
                        } else {
                            Err(ParseError::OtherError("no matching close parenthesis found.".to_owned()))
                        }
                    }
                    Err(e) => Err(e),
                }
            }
            _ => Err(ParseError::UnexpectedToken(token_list[0].to_string(), "number"))
        }
    } else {
        Err(ParseError::UnexpectedEndOfInput)
    }
}

pub fn parse(tokens: Vec<Token>) -> Result<String, ParseError> {
    e_expr(&tokens).map(|answer| answer.value.to_string())
}

fn eval(input: &str) -> String {
    match tokenize(input).and_then(parse) {
        Ok(s) => s,
        Err(_e) => "Syntax Error".to_string()
    }
}

fn main(){
    let mut window = Window::new(Rect::new(100, 100, 148, 200), "Calculator");

    {
        let text_box = TextBox::new()
            .position(0, 0)
            .size(300, 16)
            .on_enter(move |text_box: &TextBox| {
                let input = text_box.text.get();
                let result = eval(&input);
                text_box.text_i.set(result.len());
                text_box.text.set(result);
            })
            .place(&mut window);

        let mut col = 0;
        let mut row = 0;

        {
            let mut btn = |name| {
                let text_box_clone = text_box.clone();
                Button::new()
                    .position(col * 36 + 4, row * 36 + 20)
                    .size(32, 32)
                    .text(name)
                    .text_offset(12, 8)
                    .on_click(move |_button: &Button, _point: Point| {
                        let text_i = text_box_clone.text_i.get();

                        let text = text_box_clone.text.get();

                        let mut new_text = String::new();
                        new_text.push_str(&text[.. text_i]);
                        new_text.push_str(name);
                        new_text.push_str(&text[text_i ..]);

                        text_box_clone.text.set(new_text);

                        text_box_clone.text_i.set(text_i + name.len());
                    })
                    .place(&mut window);
                col += 1;
                if col >= 4 {
                    col = 0;
                    row += 1;
                }
            };

            btn("("); btn(")"); btn("^"); btn("/");
            btn("7"); btn("8"); btn("9"); btn("*");
            btn("4"); btn("5"); btn("6"); btn("-");
            btn("1"); btn("2"); btn("3"); btn("+");
            btn("0"); btn("0"); btn(".");
        }

        {
            let text_box_clone = text_box.clone();
            Button::new()
                .position(col * 36 + 4, row * 36 + 20)
                .size(32, 32)
                .text("=")
                .text_offset(12, 8)
                .on_click(move |_button: &Button, _point: Point| {
                    text_box_clone.emit_enter();
                })
                .place(&mut window);
        }
    }

    window.exec();
}
<|endoftext|>


<commit_before>use rustc_serialize::json::Json;

use client::WeChatClient;
use errors::WeChatError;

#[derive(Debug, Clone)]
pub struct WeChatSemantic<'a> {
    client: &'a WeChatClient,
}

impl<'a> WeChatSemantic<'a> {

    #[inline]
    pub fn new(client: &'a WeChatClient) -> WeChatSemantic<'a> {
        WeChatSemantic {
            client: client,
        }
    }

    pub fn search_simple(&self, query: &str, category: &str) -> Result<Json, WeChatError> {
        let body = json!({
            "query": (query),
            "category": (category),
            "appid": (self.client.appid)
        });
        self.search(&body)
    }

    pub fn search(&self, data: &Json) -> Result<Json, WeChatError> {
        let res = try!(self.client.post("https://api.weixin.qq.com/semantic/semproxy/search", vec![], data));
        Ok(res)
    }
}<commit_msg>Revert Json to Encodable<commit_after>use rustc_serialize::json::Json;
use rustc_serialize::Encodable;

use client::WeChatClient;
use errors::WeChatError;

#[derive(Debug, Clone)]
pub struct WeChatSemantic<'a> {
    client: &'a WeChatClient,
}

impl<'a> WeChatSemantic<'a> {

    #[inline]
    pub fn new(client: &'a WeChatClient) -> WeChatSemantic<'a> {
        WeChatSemantic {
            client: client,
        }
    }

    pub fn search_simple(&self, query: &str, category: &str) -> Result<Json, WeChatError> {
        let body = json!({
            "query": (query),
            "category": (category),
            "appid": (self.client.appid)
        });
        self.search(&body)
    }

    pub fn search<D: Encodable>(&self, data: &D) -> Result<Json, WeChatError> {
        let res = try!(self.client.post("https://api.weixin.qq.com/semantic/semproxy/search", vec![], data));
        Ok(res)
    }
}<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use analysis::symbols;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        if let Some(info) = info {
            try!(create_object_doc(w, env, &info));
        }
    }

    let symbols = env.symbols.borrow();
    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, &symbols));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, symbols: &symbols::Info) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, symbols),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, symbols),
        LType::Function(ref f) => create_fn_doc(w, &f, None, symbols),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let tabs = "";
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let class: &ToStripperType;
    let functions: &[Function];
    let implements: Vec<TypeId>;

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            class = cl;
            functions = &cl.functions;
            implements = cl.parents.iter()
                .chain(cl.implements.iter())
                .map(|&tid| tid)
                .collect();
        }
        Type::Interface(ref iface) => {
            class = iface;
            functions = &iface.functions;
            implements = iface.prereq_parents.clone();
        }
        _ => unreachable!(),
    }

    try!(writeln!(w, "{}{}", MOD_COMMENT, ty));

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, &symbols));
    }

    try!(write_header(w, &tabs, "Implements"));
    let implements = implements.iter()
        .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
        .map(|&tid| format!("`{}Ext`", env.library.type_(tid).get_name()))
        .collect::<Vec<_>>();
    try!(write_verbatim(w, &implements.join(", "), &tabs));

    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, &symbols));
    }

    if has_trait {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty_ext));
        try!(write_verbatim(w, &format!("Trait containing all `{}` methods.", ty.name), &tabs));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), &symbols));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, symbols: &symbols::Info) -> Result<()> {
    let ty = enum_.convert();
    let tabs = "";

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, symbols));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, symbols));
    }

    let tabs = "\t";
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, symbols));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, symbols));
        }
    }
    Ok(())
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 symbols: &symbols::Info) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;
    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &docs, &tabs, symbols));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &docs, &tabs, symbols));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &parameter_doc, &tabs, symbols));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &doc, &tabs, symbols));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str, symbols: &symbols::Info) -> Result<()> {
    write_verbatim(w, &format::reformat_doc(&lines, symbols), tabs)
}

fn write_verbatim(w: &mut Write, lines: &str, tabs: &str) -> Result<()> {
    for line in lines.split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, symbols: &symbols::Info) -> Result<()> {
    let tabs = "";
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, symbols));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, symbols));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<commit_msg>Mangle parameter names and change instance parameter to `self`<commit_after>use std::io::{Result, Write};

use analysis;
use analysis::symbols;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use nameutil;
use regex::{Captures, Regex};
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        if let Some(info) = info {
            try!(create_object_doc(w, env, &info));
        }
    }

    let symbols = env.symbols.borrow();
    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, &symbols));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, symbols: &symbols::Info) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, symbols),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, symbols),
        LType::Function(ref f) => create_fn_doc(w, &f, None, symbols),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let tabs = "";
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let class: &ToStripperType;
    let functions: &[Function];
    let implements: Vec<TypeId>;

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            class = cl;
            functions = &cl.functions;
            implements = cl.parents.iter()
                .chain(cl.implements.iter())
                .map(|&tid| tid)
                .collect();
        }
        Type::Interface(ref iface) => {
            class = iface;
            functions = &iface.functions;
            implements = iface.prereq_parents.clone();
        }
        _ => unreachable!(),
    }

    try!(writeln!(w, "{}{}", MOD_COMMENT, ty));

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, &symbols));
    }

    try!(write_header(w, &tabs, "Implements"));
    let implements = implements.iter()
        .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
        .map(|&tid| format!("`{}Ext`", env.library.type_(tid).get_name()))
        .collect::<Vec<_>>();
    try!(write_verbatim(w, &implements.join(", "), &tabs));

    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, &symbols));
    }

    if has_trait {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty_ext));
        try!(write_verbatim(w, &format!("Trait containing all `{}` methods.", ty.name), &tabs));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), &symbols));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, symbols: &symbols::Info) -> Result<()> {
    let ty = enum_.convert();
    let tabs = "";

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, symbols));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, symbols));
    }

    let tabs = "\t";
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, symbols));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, symbols));
        }
    }
    Ok(())
}

lazy_static! {
    static ref PARAM_NAME: Regex = Regex::new(r"@(\w+)\b").unwrap();
}

fn fix_param_names(doc: &str, self_name: &Option<String>) -> String {
    PARAM_NAME.replace_all(doc, |caps: &Captures| {
        if let Some(ref self_name) = *self_name {
            if &caps[1] == self_name {
                return "@self".into()
            }
        }
        format!("@{}", nameutil::mangle_keywords(&caps[1]))
    })
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 symbols: &symbols::Info) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;

    let self_name: Option<String> = fn_.parameters.iter()
        .find(|p| p.instance_parameter)
        .map(|p| p.name.clone());

    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &fix_param_names(docs, &self_name), &tabs, symbols));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &fix_param_names(docs, &self_name), &tabs, symbols));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &fix_param_names(parameter_doc, &self_name), &tabs, symbols));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &fix_param_names(doc, &self_name), &tabs, symbols));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str, symbols: &symbols::Info) -> Result<()> {
    write_verbatim(w, &format::reformat_doc(&lines, symbols), tabs)
}

fn write_verbatim(w: &mut Write, lines: &str, tabs: &str) -> Result<()> {
    for line in lines.split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, symbols: &symbols::Info) -> Result<()> {
    let tabs = "";
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, symbols));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, symbols));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<|endoftext|>


<commit_before>// TODO: Make this non public
pub mod file_naming;

use time_utils;
use time_utils::to_pretty_local;
use std::collections::HashMap;
use std::fmt::{Display, Error, Formatter};
use time::Timespec;
use self::file_naming::{FileName, FileNameInfo, FileType, FileNameParser};

pub struct BackupSet {
    pub file_type: FileType,
    pub time: Timespec,
    pub start_time: Timespec,
    pub end_time: Timespec,
    pub compressed: bool,
    pub encrypted: bool,
    pub partial: bool,
    pub manifest_path: String,
    volumes_paths: HashMap<i32, String>,
    info_set: bool,    // true if informations are set
}

impl BackupSet {
    pub fn new() -> Self {
        BackupSet{
            file_type: FileType::Full,
            time: time_utils::DEFAULT_TIMESPEC,
            start_time: time_utils::DEFAULT_TIMESPEC,
            end_time: time_utils::DEFAULT_TIMESPEC,
            compressed: false,
            encrypted: false,
            partial: false,
            manifest_path: String::new(),
            volumes_paths: HashMap::new(),
            info_set: false
        }
    }

    /// Add a filename to given set. Return true if it fits.
    ///
    /// The filename will match the given set if it has the right
    /// times and is of the right type. The information will be set
    /// from the first filename given.
    pub fn add_filename(&mut self, file_info: &FileNameInfo) -> bool {
        let pr = &file_info.info;
        let fname = file_info.file_name;

        if !self.info_set {
            self.set_info(&pr);
        }
        else {
            // check if the file is from the same backup set
            if self.file_type != pr.file_type ||
                self.time != pr.time ||
                self.start_time != pr.start_time ||
                self.end_time != pr.end_time {
                    return false;
            }
            else {
                // fix encrypted flag
                if self.encrypted != pr.encrypted &&
                    self.partial && pr.encrypted {
                        self.encrypted = pr.encrypted;
                    }
            }
        }
        // set manifest or volume number
        if pr.manifest {
            self.manifest_path = fname.to_owned();
        }
        else {
            self.volumes_paths.insert(pr.volume_number, fname.to_owned());
        }
        true
    }

    pub fn is_complete(&self) -> bool {
        !self.manifest_path.is_empty()
    }

    pub fn get_time(&self) -> &Timespec {
        if self.time == time_utils::DEFAULT_TIMESPEC {
            &self.end_time
        }
        else {
            &self.time
        }
    }

    fn set_info(&mut self, fname: &FileName) {
        self.file_type = fname.file_type;
        self.time = fname.time.clone();
        self.start_time = fname.start_time.clone();
        self.end_time = fname.end_time.clone();
        self.compressed = fname.compressed;
        self.encrypted = fname.encrypted;
        self.partial = fname.partial;
        self.info_set = true;
    }
}

impl Display for BackupSet {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match self.file_type {
            FileType::Full => {
                try!(write!(f, "Full, time: {}", to_pretty_local(self.time)));
            },
            FileType::Inc => {
                try!(write!(f, "Incremental, start time: {}, end time: {}",
                            to_pretty_local(self.start_time),
                            to_pretty_local(self.end_time)));
            },
            _ => { }
        }
        if self.compressed {
            try!(write!(f, ", compressed"));
        }
        if self.encrypted {
            try!(write!(f, ", encrypted"));
        }
        if self.partial {
            try!(write!(f, ", partial"));
        }
        if !self.manifest_path.is_empty() {
            try!(write!(f, "\n manifest: {}", self.manifest_path));
        }
        if !self.volumes_paths.is_empty() {
            try!(write!(f, "\n volumes:\n"));
            for (_, vol) in self.volumes_paths.iter() {
                try!(write!(f, " {}", vol));
            }
        }
        Ok(())
    }
}

pub struct BackupChain {
    pub fullset: BackupSet,
    pub incset_list: Vec<BackupSet>,
    pub start_time: Timespec,
    pub end_time: Timespec
}

impl BackupChain {
    /// Create a new BackupChain starting from a full backup set.
    pub fn new(fullset: BackupSet) -> Self {
        assert_eq!(fullset.file_type, FileType::Full);
        let time = fullset.time.clone();

        BackupChain{
            fullset: fullset,
            incset_list: Vec::new(),
            start_time: time,
            end_time: time
        }
    }

    /// Adds the given incremental backup element to the backup chain if possible,
    /// returns it back otherwise.
    pub fn add_inc(&mut self, incset: BackupSet) -> Option<BackupSet> {
        if self.end_time == incset.start_time {
            self.end_time = incset.end_time.clone();
            self.incset_list.push(incset);
            None
        }
        else {
            // replace the last element if the end time comes before
            let replace_last = self.incset_list.last().map_or(false,
                |last| incset.start_time == last.start_time && incset.end_time > last.end_time);
            if replace_last {
                self.end_time = incset.end_time.clone();
                self.incset_list.pop();
                self.incset_list.push(incset);
                None
            }
            else {
                // ignore the given incremental backup set
                Some(incset)
            }
        }
    }
}

impl Display for BackupChain {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        try!(write!(f, "start time: {}, end time: {}\n{}",
                    to_pretty_local(self.start_time),
                    to_pretty_local(self.end_time),
                    &self.fullset));
        for inc in self.incset_list.iter() {
            try!(write!(f, "\n{}", inc));
        }
        Ok(())
    }
}

pub struct SignatureChain {
    pub fullsig: String,
    pub inclist: Vec<String>,
    pub start_time: Timespec,
    pub end_time: Timespec
}

impl SignatureChain {
    /// Create a new SignatureChain starting from a full signature.
    pub fn new(fname: &str, pr: &FileName) -> Self {
        SignatureChain {
            fullsig: fname.to_owned(),
            inclist: Vec::new(),
            start_time: pr.time,
            end_time: pr.time
        }
    }

    pub fn from_filename_info(fname_info: &FileNameInfo) -> Self {
        Self::new(fname_info.file_name, &fname_info.info)
    }

    /// Adds the given incremental signature to the signature chain if possible,
    /// returns false otherwise.
    pub fn add_new_sig(&mut self, fname: &FileNameInfo) -> bool {
        if fname.info.file_type != FileType::NewSig {
            false
        }
        else {
            self.inclist.push(fname.file_name.to_owned());
            self.end_time = fname.info.end_time.clone();
            true
        }
    }
}

impl Display for SignatureChain {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        try!(write!(f, "start time: {}, end time: {}\n{}",
                    to_pretty_local(self.start_time),
                    to_pretty_local(self.end_time),
                    &self.fullsig));
        for inc in self.inclist.iter() {
            try!(write!(f, "\n{}", inc));
        }
        Ok(())
    }
}

type FileNameInfos<'a> = Vec<FileNameInfo<'a>>;

pub struct CollectionsStatus {
    pub backup_chains: Vec<BackupChain>,
    pub sig_chains: Vec<SignatureChain>
}

impl CollectionsStatus {
    pub fn new() -> Self {
        CollectionsStatus {
            backup_chains: Vec::new(),
            sig_chains: Vec::new()
        }
    }

    pub fn from_filenames<T: AsRef<str>>(filenames: &[T]) -> Self {
        let mut result = Self::new();
        let filename_infos = Self::compute_filename_infos(&filenames);
        result.compute_backup_chains(&filename_infos);
        result.compute_signature_chains(&filename_infos);
        result
    }

    fn compute_filename_infos<'a, T: AsRef<str>>(filename_list: &'a [T]) -> FileNameInfos<'a> {
        let mut result = Vec::new();
        let parser = FileNameParser::new();
        for name in filename_list {
            if let Some(info) = parser.parse(name.as_ref()) {
                result.push(FileNameInfo::new(name.as_ref(), info));
            }
        }
        result
    }

    fn compute_backup_chains(&mut self, filename_list: &FileNameInfos) {
        let sets = Self::compute_backup_sets(filename_list);
        self.add_to_backup_chains(sets);
    }

    fn compute_backup_sets(filename_list: &FileNameInfos) -> Vec<BackupSet> {
        let mut sets = Vec::<BackupSet>::new();
        for fileinfo in filename_list.iter() {
            let mut inserted = false;
            for set in sets.iter_mut() {
                if set.add_filename(&fileinfo) {
                    inserted = true;
                    break;
                }
            }
            if !inserted {
                let mut new_set = BackupSet::new();
                new_set.add_filename(&fileinfo);
                sets.push(new_set);
            }
        }
        // sort by time
        sets.sort_by(|a, b| a.get_time().cmp(b.get_time()));
        sets
    }

    fn add_to_backup_chains(&mut self, set_list: Vec<BackupSet>) {
        for set in set_list.into_iter() {
            match set.file_type {
                FileType::Full => {
                    let new_chain = BackupChain::new(set);
                    self.backup_chains.push(new_chain);
                }
                FileType::Inc => {
                    let mut rejected_set = Some(set);
                    for chain in self.backup_chains.iter_mut() {
                        rejected_set = chain.add_inc(rejected_set.unwrap());
                        if rejected_set.is_none() {
                            break;
                        }
                    }
                    if let Some(_) = rejected_set {
                        // TODO: add to orphaned sets
                    }
                }
                _ => { continue; }
            }
        }
        // sort by end time
        self.backup_chains.sort_by(|a, b| a.end_time.cmp(&b.end_time));
    }

    fn compute_signature_chains(&mut self, filename_list: &FileNameInfos) {
        // create a new signature chain for each fill signature
        self.sig_chains = filename_list.iter()
            .filter(|f| f.info.file_type == FileType::FullSig)
            .map(|f| SignatureChain::from_filename_info(f))
            .collect();
        // and collect all the new signatures, sorted by start time
        let mut new_sig = filename_list.iter()
            .filter(|f| f.info.file_type == FileType::NewSig)
            .collect::<Vec<_>>();
        new_sig.sort_by(|a, b| a.info.start_time.cmp(&b.info.start_time));

        // add the new signatures to signature chains
        for sig in new_sig.into_iter() {
            let mut added = false;
            for chain in self.sig_chains.iter_mut() {
                if chain.add_new_sig(&sig) {
                    added = true;
                    break;
                }
            }
            if !added {
                // TODO: add to orphaned filenames
            }
        }
    }
}

impl Display for CollectionsStatus {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        for backup_chain in &self.backup_chains {
            try!(backup_chain.fmt(f));
        }
        try!(write!(f, "\nsignature chains:\n"));
        for signature_chain in &self.sig_chains {
            try!(signature_chain.fmt(f));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::{BackupSet, CollectionsStatus};
    use super::file_naming::{FileType, FileNameInfo, FileNameParser};
    use time_utils::{DEFAULT_TIMESPEC, parse_time_str};

    #[test]
    fn parse_and_add() {
        let full1_name = "duplicity-full.20150617T182545Z.vol1.difftar.gz";
        let manifest1_name = "duplicity-full.20150617T182545Z.manifest";
        let inc1_name = "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz";

        let parser = FileNameParser::new();
        let full1 = FileNameInfo::new(full1_name, parser.parse(full1_name).unwrap());
        let manifest1 = FileNameInfo::new(manifest1_name, parser.parse(manifest1_name).unwrap());
        let inc1 = FileNameInfo::new(inc1_name, parser.parse(inc1_name).unwrap());

        let mut set = BackupSet::new();
        // add to set
        assert!(set.add_filename(&full1));
        assert!(set.add_filename(&manifest1));
        assert!(!set.add_filename(&inc1));
        // test results
        assert_eq!(set.file_type, FileType::Full);
        assert_eq!(set.time, parse_time_str("20150617t182545z").unwrap());
        assert_eq!(set.start_time, DEFAULT_TIMESPEC);
        assert_eq!(set.end_time, DEFAULT_TIMESPEC);
        assert!(set.compressed);
        assert!(!set.encrypted);
        assert!(!set.partial);
        assert_eq!(set.manifest_path, manifest1_name);
    }

    #[test]
    fn collection_status() {
        let filenames = vec![
            "duplicity-full.20150617T182545Z.manifest",
            "duplicity-full.20150617T182545Z.vol1.difftar.gz",
            "duplicity-full-signatures.20150617T182545Z.sigtar.gz",
            "duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest",
            "duplicity-inc.20150617T182545Z.to.20150617T182629Z.vol1.difftar.gz",
            "duplicity-inc.20150617T182629Z.to.20150617T182650Z.manifest",
            "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz",
            "duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz",
            "duplicity-new-signatures.20150617T182629Z.to.20150617T182650Z.sigtar.gz"
        ];
        let collection_status = CollectionsStatus::from_filenames(&filenames);
        println!("collection: {}", collection_status);
    }
}
<commit_msg>Improved collection status interface<commit_after>// TODO: Make this non public
pub mod file_naming;

use time_utils;
use time_utils::to_pretty_local;
use std::collections::HashMap;
use std::fmt::{Display, Error, Formatter};
use std::slice;
use time::Timespec;
use self::file_naming::{FileName, FileNameInfo, FileType, FileNameParser};

pub struct BackupSet {
    pub file_type: FileType,
    pub time: Timespec,
    pub start_time: Timespec,
    pub end_time: Timespec,
    pub compressed: bool,
    pub encrypted: bool,
    pub partial: bool,
    pub manifest_path: String,
    volumes_paths: HashMap<i32, String>,
    info_set: bool,    // true if informations are set
}

impl BackupSet {
    pub fn new() -> Self {
        BackupSet{
            file_type: FileType::Full,
            time: time_utils::DEFAULT_TIMESPEC,
            start_time: time_utils::DEFAULT_TIMESPEC,
            end_time: time_utils::DEFAULT_TIMESPEC,
            compressed: false,
            encrypted: false,
            partial: false,
            manifest_path: String::new(),
            volumes_paths: HashMap::new(),
            info_set: false
        }
    }

    /// Add a filename to given set. Return true if it fits.
    ///
    /// The filename will match the given set if it has the right
    /// times and is of the right type. The information will be set
    /// from the first filename given.
    pub fn add_filename(&mut self, file_info: &FileNameInfo) -> bool {
        let pr = &file_info.info;
        let fname = file_info.file_name;

        if !self.info_set {
            self.set_info(&pr);
        }
        else {
            // check if the file is from the same backup set
            if self.file_type != pr.file_type ||
                self.time != pr.time ||
                self.start_time != pr.start_time ||
                self.end_time != pr.end_time {
                    return false;
            }
            else {
                // fix encrypted flag
                if self.encrypted != pr.encrypted &&
                    self.partial && pr.encrypted {
                        self.encrypted = pr.encrypted;
                    }
            }
        }
        // set manifest or volume number
        if pr.manifest {
            self.manifest_path = fname.to_owned();
        }
        else {
            self.volumes_paths.insert(pr.volume_number, fname.to_owned());
        }
        true
    }

    pub fn is_complete(&self) -> bool {
        !self.manifest_path.is_empty()
    }

    pub fn get_time(&self) -> &Timespec {
        if self.time == time_utils::DEFAULT_TIMESPEC {
            &self.end_time
        }
        else {
            &self.time
        }
    }

    fn set_info(&mut self, fname: &FileName) {
        self.file_type = fname.file_type;
        self.time = fname.time.clone();
        self.start_time = fname.start_time.clone();
        self.end_time = fname.end_time.clone();
        self.compressed = fname.compressed;
        self.encrypted = fname.encrypted;
        self.partial = fname.partial;
        self.info_set = true;
    }
}

impl Display for BackupSet {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match self.file_type {
            FileType::Full => {
                try!(write!(f, "Full, time: {}", to_pretty_local(self.time)));
            },
            FileType::Inc => {
                try!(write!(f, "Incremental, start time: {}, end time: {}",
                            to_pretty_local(self.start_time),
                            to_pretty_local(self.end_time)));
            },
            _ => { }
        }
        if self.compressed {
            try!(write!(f, ", compressed"));
        }
        if self.encrypted {
            try!(write!(f, ", encrypted"));
        }
        if self.partial {
            try!(write!(f, ", partial"));
        }
        if !self.manifest_path.is_empty() {
            try!(write!(f, "\n manifest: {}", self.manifest_path));
        }
        if !self.volumes_paths.is_empty() {
            try!(write!(f, "\n volumes:\n"));
            for (_, vol) in self.volumes_paths.iter() {
                try!(write!(f, " {}", vol));
            }
        }
        Ok(())
    }
}

pub struct BackupChain {
    pub fullset: BackupSet,
    pub incset_list: Vec<BackupSet>,
    pub start_time: Timespec,
    pub end_time: Timespec
}

impl BackupChain {
    /// Create a new BackupChain starting from a full backup set.
    pub fn new(fullset: BackupSet) -> Self {
        assert_eq!(fullset.file_type, FileType::Full);
        let time = fullset.time.clone();

        BackupChain{
            fullset: fullset,
            incset_list: Vec::new(),
            start_time: time,
            end_time: time
        }
    }

    /// Adds the given incremental backup element to the backup chain if possible,
    /// returns it back otherwise.
    pub fn add_inc(&mut self, incset: BackupSet) -> Option<BackupSet> {
        if self.end_time == incset.start_time {
            self.end_time = incset.end_time.clone();
            self.incset_list.push(incset);
            None
        }
        else {
            // replace the last element if the end time comes before
            let replace_last = self.incset_list.last().map_or(false,
                |last| incset.start_time == last.start_time && incset.end_time > last.end_time);
            if replace_last {
                self.end_time = incset.end_time.clone();
                self.incset_list.pop();
                self.incset_list.push(incset);
                None
            }
            else {
                // ignore the given incremental backup set
                Some(incset)
            }
        }
    }
}

impl Display for BackupChain {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        try!(write!(f, "start time: {}, end time: {}\n{}",
                    to_pretty_local(self.start_time),
                    to_pretty_local(self.end_time),
                    &self.fullset));
        for inc in self.incset_list.iter() {
            try!(write!(f, "\n{}", inc));
        }
        Ok(())
    }
}

pub struct SignatureChain {
    pub fullsig: String,
    pub inclist: Vec<String>,
    pub start_time: Timespec,
    pub end_time: Timespec
}

impl SignatureChain {
    /// Create a new SignatureChain starting from a full signature.
    pub fn new(fname: &str, pr: &FileName) -> Self {
        SignatureChain {
            fullsig: fname.to_owned(),
            inclist: Vec::new(),
            start_time: pr.time,
            end_time: pr.time
        }
    }

    pub fn from_filename_info(fname_info: &FileNameInfo) -> Self {
        Self::new(fname_info.file_name, &fname_info.info)
    }

    /// Adds the given incremental signature to the signature chain if possible,
    /// returns false otherwise.
    pub fn add_new_sig(&mut self, fname: &FileNameInfo) -> bool {
        if fname.info.file_type != FileType::NewSig {
            false
        }
        else {
            self.inclist.push(fname.file_name.to_owned());
            self.end_time = fname.info.end_time.clone();
            true
        }
    }
}

impl Display for SignatureChain {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        try!(write!(f, "start time: {}, end time: {}\n{}",
                    to_pretty_local(self.start_time),
                    to_pretty_local(self.end_time),
                    &self.fullsig));
        for inc in self.inclist.iter() {
            try!(write!(f, "\n{}", inc));
        }
        Ok(())
    }
}

type FileNameInfos<'a> = Vec<FileNameInfo<'a>>;
pub type BackupChainIterator<'a> = slice::Iter<'a, BackupChain>;
pub type SignatureChainIterator<'a> = slice::Iter<'a, SignatureChain>;

pub struct CollectionsStatus {
    backup_chains: Vec<BackupChain>,
    sig_chains: Vec<SignatureChain>
}

impl CollectionsStatus {
    pub fn new() -> Self {
        CollectionsStatus {
            backup_chains: Vec::new(),
            sig_chains: Vec::new()
        }
    }

    pub fn from_filenames<T: AsRef<str>>(filenames: &[T]) -> Self {
        let mut result = Self::new();
        let filename_infos = Self::compute_filename_infos(&filenames);
        result.compute_backup_chains(&filename_infos);
        result.compute_signature_chains(&filename_infos);
        result
    }

    pub fn backup_chains(&self) -> BackupChainIterator {
        self.backup_chains.iter()
    }

    pub fn signature_chains(&self) -> SignatureChainIterator {
        self.sig_chains.iter()
    }

    fn compute_filename_infos<'a, T: AsRef<str>>(filename_list: &'a [T]) -> FileNameInfos<'a> {
        let mut result = Vec::new();
        let parser = FileNameParser::new();
        for name in filename_list {
            if let Some(info) = parser.parse(name.as_ref()) {
                result.push(FileNameInfo::new(name.as_ref(), info));
            }
        }
        result
    }

    fn compute_backup_chains(&mut self, filename_list: &FileNameInfos) {
        let sets = Self::compute_backup_sets(filename_list);
        self.add_to_backup_chains(sets);
    }

    fn compute_backup_sets(filename_list: &FileNameInfos) -> Vec<BackupSet> {
        let mut sets = Vec::<BackupSet>::new();
        for fileinfo in filename_list.iter() {
            let mut inserted = false;
            for set in sets.iter_mut() {
                if set.add_filename(&fileinfo) {
                    inserted = true;
                    break;
                }
            }
            if !inserted {
                let mut new_set = BackupSet::new();
                new_set.add_filename(&fileinfo);
                sets.push(new_set);
            }
        }
        // sort by time
        sets.sort_by(|a, b| a.get_time().cmp(b.get_time()));
        sets
    }

    fn add_to_backup_chains(&mut self, set_list: Vec<BackupSet>) {
        for set in set_list.into_iter() {
            match set.file_type {
                FileType::Full => {
                    let new_chain = BackupChain::new(set);
                    self.backup_chains.push(new_chain);
                }
                FileType::Inc => {
                    let mut rejected_set = Some(set);
                    for chain in self.backup_chains.iter_mut() {
                        rejected_set = chain.add_inc(rejected_set.unwrap());
                        if rejected_set.is_none() {
                            break;
                        }
                    }
                    if let Some(_) = rejected_set {
                        // TODO: add to orphaned sets
                    }
                }
                _ => { continue; }
            }
        }
        // sort by end time
        self.backup_chains.sort_by(|a, b| a.end_time.cmp(&b.end_time));
    }

    fn compute_signature_chains(&mut self, filename_list: &FileNameInfos) {
        // create a new signature chain for each fill signature
        self.sig_chains = filename_list.iter()
            .filter(|f| f.info.file_type == FileType::FullSig)
            .map(|f| SignatureChain::from_filename_info(f))
            .collect();
        // and collect all the new signatures, sorted by start time
        let mut new_sig: Vec<_> = filename_list.iter()
            .filter(|f| f.info.file_type == FileType::NewSig)
            .collect();
        new_sig.sort_by(|a, b| a.info.start_time.cmp(&b.info.start_time));

        // add the new signatures to signature chains
        for sig in new_sig.into_iter() {
            let mut added = false;
            for chain in self.sig_chains.iter_mut() {
                if chain.add_new_sig(&sig) {
                    added = true;
                    break;
                }
            }
            if !added {
                // TODO: add to orphaned filenames
            }
        }
    }
}

impl Display for CollectionsStatus {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        for backup_chain in &self.backup_chains {
            try!(backup_chain.fmt(f));
        }
        try!(write!(f, "\nsignature chains:\n"));
        for signature_chain in &self.sig_chains {
            try!(signature_chain.fmt(f));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::{BackupSet, CollectionsStatus};
    use super::file_naming::{FileType, FileNameInfo, FileNameParser};
    use time_utils::{DEFAULT_TIMESPEC, parse_time_str};

    #[test]
    fn parse_and_add() {
        let full1_name = "duplicity-full.20150617T182545Z.vol1.difftar.gz";
        let manifest1_name = "duplicity-full.20150617T182545Z.manifest";
        let inc1_name = "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz";

        let parser = FileNameParser::new();
        let full1 = FileNameInfo::new(full1_name, parser.parse(full1_name).unwrap());
        let manifest1 = FileNameInfo::new(manifest1_name, parser.parse(manifest1_name).unwrap());
        let inc1 = FileNameInfo::new(inc1_name, parser.parse(inc1_name).unwrap());

        let mut set = BackupSet::new();
        // add to set
        assert!(set.add_filename(&full1));
        assert!(set.add_filename(&manifest1));
        assert!(!set.add_filename(&inc1));
        // test results
        assert_eq!(set.file_type, FileType::Full);
        assert_eq!(set.time, parse_time_str("20150617t182545z").unwrap());
        assert_eq!(set.start_time, DEFAULT_TIMESPEC);
        assert_eq!(set.end_time, DEFAULT_TIMESPEC);
        assert!(set.compressed);
        assert!(!set.encrypted);
        assert!(!set.partial);
        assert_eq!(set.manifest_path, manifest1_name);
    }

    #[test]
    fn collection_status() {
        let filenames = vec![
            "duplicity-full.20150617T182545Z.manifest",
            "duplicity-full.20150617T182545Z.vol1.difftar.gz",
            "duplicity-full-signatures.20150617T182545Z.sigtar.gz",
            "duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest",
            "duplicity-inc.20150617T182545Z.to.20150617T182629Z.vol1.difftar.gz",
            "duplicity-inc.20150617T182629Z.to.20150617T182650Z.manifest",
            "duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz",
            "duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz",
            "duplicity-new-signatures.20150617T182629Z.to.20150617T182650Z.sigtar.gz"
        ];
        let collection_status = CollectionsStatus::from_filenames(&filenames);
        println!("collection: {}", collection_status);
    }
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Comma_quibbling
#![feature(collections)]
#![feature(slice_patterns)]
fn quibble(seq: &[&str]) -> String {
    match seq {
        [] => "{}".to_string(),
        [word] => format!("{{{}}}", word ),
        _ => format!("{{{} and {}}}", seq.init().connect(", "), seq.last().unwrap())
    }
}

#[cfg(not(test))]
fn main() {
    println!("{}", quibble(&[]));
    println!("{}", quibble(&["ABC"]));
    println!("{}", quibble(&["ABC", "DEF"]));
    println!("{}", quibble(&["ABC", "DEF", "G", "H"]));
}

#[test]
fn output() {
    assert_eq!(quibble(&[]), "{}");
    assert_eq!(quibble(&["ABC"]), "{ABC}");
    assert_eq!(quibble(&["ABC", "DEF"]), "{ABC and DEF}");
    assert_eq!(quibble(&["ABC", "DEF", "G", "H"]), "{ABC, DEF, G and H}");
}
<commit_msg>de-featured comma quibbling<commit_after>// http://rosettacode.org/wiki/Comma_quibbling
fn quibble(seq: &[&str]) -> String {
    match seq.len() {
        0 => "{}".to_string(),
        1 => format!("{{{}}}", seq[0] ),
        _ => format!("{{{} and {}}}", seq[.. seq.len() - 1].connect(", "), seq.last().unwrap())
    }
}

#[cfg(not(test))]
fn main() {
    println!("{}", quibble(&[]));
    println!("{}", quibble(&["ABC"]));
    println!("{}", quibble(&["ABC", "DEF"]));
    println!("{}", quibble(&["ABC", "DEF", "G", "H"]));
}

#[test]
fn output() {
    assert_eq!(quibble(&[]), "{}");
    assert_eq!(quibble(&["ABC"]), "{ABC}");
    assert_eq!(quibble(&["ABC", "DEF"]), "{ABC and DEF}");
    assert_eq!(quibble(&["ABC", "DEF", "G", "H"]), "{ABC, DEF, G and H}");
}
<|endoftext|>