<commit_before>use nom::multispace;
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use column::Column;
use common::FieldExpression;
use common::{field_expr, field_list, unsigned_number, statement_terminator, table_list};
use condition::{condition_expr, ConditionExpression};
use table::Table;

#[derive(Clone, Debug, PartialEq)]
pub struct GroupByClause {
    columns: Vec<Column>,
    having: Option<ConditionExpression>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct LimitClause {
    limit: u64,
    offset: u64,
}

#[derive(Clone, Debug, PartialEq)]
pub enum OrderType {
    OrderAscending,
    OrderDescending,
}

#[derive(Clone, Debug, PartialEq)]
pub struct OrderClause {
    cols: Vec<Column>, // TODO(malte): can this be an arbitrary expr?
    order: OrderType,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct SelectStatement {
    pub tables: Vec<Table>,
    pub distinct: bool,
    pub fields: FieldExpression,
    pub where_clause: Option<ConditionExpression>,
    pub group_by: Option<GroupByClause>,
    pub order: Option<OrderClause>,
    pub limit: Option<LimitClause>,
}

/// Parse GROUP BY clause
named!(group_by_clause<&[u8], GroupByClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("group by") ~
        multispace ~
        group_columns: field_list ~
        having_clause: opt!(
            complete!(chain!(
                multispace? ~
                caseless_tag!("having") ~
                multispace? ~
                ce: condition_expr,
                || { ce }
            ))
        ),
    || {
        GroupByClause {
            columns: group_columns,
            having: having_clause,
        }
    }))
);

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("limit") ~
        multispace ~
        limit_val: unsigned_number ~
        offset_val: opt!(
            complete!(chain!(
                multispace? ~
                caseless_tag!("offset") ~
                multispace ~
                val: unsigned_number,
                || { val }
            ))
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    }))
);

named!(order_clause<&[u8], OrderClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("order by") ~
        multispace ~
        order_expr: field_list ~
        ordering: opt!(
            complete!(chain!(
                multispace? ~
                ordering: alt_complete!(
                      map!(caseless_tag!("desc"), |_| OrderType::OrderDescending)
                    | map!(caseless_tag!("asc"), |_| OrderType::OrderAscending)
                ),
                || { ordering }
            ))
        ),
    || {
        OrderClause {
            cols: order_expr,
            order: match ordering {
                None => OrderType::OrderAscending,
                Some(ref o) => o.clone(),
            },
        }
    }))
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionExpression>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("where") ~
        multispace ~
        cond: condition_expr,
        || { cond }
    ))
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    chain!(
        caseless_tag!("select") ~
        multispace ~
        distinct: opt!(caseless_tag!("distinct")) ~
        multispace? ~
        fields: field_expr ~
        delimited!(opt!(multispace), caseless_tag!("from"), opt!(multispace)) ~
        tables: table_list ~
        cond: opt!(where_clause) ~
        group_by: opt!(group_by_clause) ~
        order: opt!(order_clause) ~
        limit: opt!(limit_clause) ~
        statement_terminator,
        || {
            SelectStatement {
                tables: tables,
                distinct: distinct.is_some(),
                fields: fields,
                where_clause: cond,
                group_by: group_by,
                order: order,
                limit: limit,
            }
        }
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::{Column, FunctionExpression};
    use common::{FieldExpression, Operator};
    use condition::{ConditionBase, ConditionExpression, ConditionTree};
    use table::Table;

    fn columns(cols: &[&str]) -> Vec<Column> {
        cols.iter()
            .map(|c| Column::from(*c))
            .collect()
    }

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::Seq(columns(&["id", "name"])),
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::All,
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::Seq(columns(&["id", "name"])),
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        let r1 = selection(qstring_sem.as_bytes()).unwrap();
        let r2 = selection(qstring_linebreak.as_bytes()).unwrap();
        assert_eq!(r1, r2);
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?;";

        let res = selection(qstring.as_bytes());

        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("email"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("ContactInfo")],
                       fields: FieldExpression::All,
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }

    #[test]
    fn order_clause() {
        let qstring1 = "select * from users order by name desc\n";
        let qstring2 = "select * from users order by name, age desc\n";
        let qstring3 = "select * from users order by name\n";

        let expected_ord1 = OrderClause {
            cols: columns(&["name"]),
            order: OrderType::OrderDescending,
        };
        let expected_ord2 = OrderClause {
            cols: columns(&["name", "age"]),
            order: OrderType::OrderDescending,
        };
        let expected_ord3 = OrderClause {
            cols: columns(&["name"]),
            order: OrderType::OrderAscending,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        let res3 = selection(qstring3.as_bytes());
        assert_eq!(res1.unwrap().1.order, Some(expected_ord1));
        assert_eq!(res2.unwrap().1.order, Some(expected_ord2));
        assert_eq!(res3.unwrap().1.order, Some(expected_ord3));
    }

    #[test]
    fn table_alias() {
        let qstring1 = "select * from PaperTag as t;";
        // let qstring2 = "select * from PaperTag t;";

        let res1 = selection(qstring1.as_bytes());
        assert_eq!(res1.clone().unwrap().1,
                   SelectStatement {
                       tables: vec![Table {
                           name: String::from("PaperTag"),
                           alias: Some(String::from("t")),
                       }],
                       fields: FieldExpression::All,
                       ..Default::default()
                   });
        // let res2 = selection(qstring2.as_bytes());
        // assert_eq!(res1.unwrap().1, res2.unwrap().1);
    }

    #[test]
    fn distinct() {
        let qstring = "select distinct tag from PaperTag where paperId=?;";

        let res = selection(qstring.as_bytes());
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("PaperTag")],
                       distinct: true,
                       fields: FieldExpression::Seq(columns(&["tag"])),
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn simple_condition_expr() {
        let qstring = "select infoJson from PaperStorage where paperId=? and paperStorageId=?;";

        let res = selection(qstring.as_bytes());

        let left_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        })));
        let right_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperStorageId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        })));
        let expected_where_cond = Some(ConditionExpression::LogicalOp(ConditionTree {
            left: left_comp,
            right: right_comp,
            operator: Operator::And,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("PaperStorage")],
                       fields: FieldExpression::Seq(columns(&["infoJson"])),
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn where_and_limit_clauses() {
        let qstring = "select * from users where id = ? limit 10\n";
        let res = selection(qstring.as_bytes());

        let expected_lim = Some(LimitClause {
            limit: 10,
            offset: 0,
        });
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("id"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));

        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::All,
                       where_clause: expected_where_cond,
                       limit: expected_lim,
                       ..Default::default()
                   });
    }

    #[test]
    fn aggregation_column() {
        let qstring = "SELECT max(addr_id) FROM address;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Max(FieldExpression::Seq(vec![Column::from("addr_id")]));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("address")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("anon_fn"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       ..Default::default()
                   });
    }

    #[test]
    fn aggregation_column_with_alias() {
        let qstring = "SELECT max(addr_id) AS max_addr FROM address;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Max(FieldExpression::Seq(vec![Column::from("addr_id")]));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("address")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("max_addr"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       ..Default::default()
                   });
    }

    #[test]
    fn count_all() {
        let qstring = "SELECT COUNT(*) FROM votes GROUP BY aid;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Count(FieldExpression::All);
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("votes")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("anon_fn"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       group_by: Some(GroupByClause {
                           columns: vec![Column::from("aid")],
                           having: None,
                       }),
                       ..Default::default()
                   });
    }
}
<commit_msg>Make fields in subclause structures public<commit_after>use nom::multispace;
use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

use column::Column;
use common::FieldExpression;
use common::{field_expr, field_list, unsigned_number, statement_terminator, table_list};
use condition::{condition_expr, ConditionExpression};
use table::Table;

#[derive(Clone, Debug, PartialEq)]
pub struct GroupByClause {
    pub columns: Vec<Column>,
    pub having: Option<ConditionExpression>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct LimitClause {
    pub limit: u64,
    pub offset: u64,
}

#[derive(Clone, Debug, PartialEq)]
pub enum OrderType {
    OrderAscending,
    OrderDescending,
}

#[derive(Clone, Debug, PartialEq)]
pub struct OrderClause {
    pub cols: Vec<Column>, // TODO(malte): can this be an arbitrary expr?
    pub order: OrderType,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct SelectStatement {
    pub tables: Vec<Table>,
    pub distinct: bool,
    pub fields: FieldExpression,
    pub where_clause: Option<ConditionExpression>,
    pub group_by: Option<GroupByClause>,
    pub order: Option<OrderClause>,
    pub limit: Option<LimitClause>,
}

/// Parse GROUP BY clause
named!(group_by_clause<&[u8], GroupByClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("group by") ~
        multispace ~
        group_columns: field_list ~
        having_clause: opt!(
            complete!(chain!(
                multispace? ~
                caseless_tag!("having") ~
                multispace? ~
                ce: condition_expr,
                || { ce }
            ))
        ),
    || {
        GroupByClause {
            columns: group_columns,
            having: having_clause,
        }
    }))
);

/// Parse LIMIT clause
named!(limit_clause<&[u8], LimitClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("limit") ~
        multispace ~
        limit_val: unsigned_number ~
        offset_val: opt!(
            complete!(chain!(
                multispace? ~
                caseless_tag!("offset") ~
                multispace ~
                val: unsigned_number,
                || { val }
            ))
        ),
    || {
        LimitClause {
            limit: limit_val,
            offset: match offset_val {
                None => 0,
                Some(v) => v,
            },
        }
    }))
);

named!(order_clause<&[u8], OrderClause>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("order by") ~
        multispace ~
        order_expr: field_list ~
        ordering: opt!(
            complete!(chain!(
                multispace? ~
                ordering: alt_complete!(
                      map!(caseless_tag!("desc"), |_| OrderType::OrderDescending)
                    | map!(caseless_tag!("asc"), |_| OrderType::OrderAscending)
                ),
                || { ordering }
            ))
        ),
    || {
        OrderClause {
            cols: order_expr,
            order: match ordering {
                None => OrderType::OrderAscending,
                Some(ref o) => o.clone(),
            },
        }
    }))
);

/// Parse WHERE clause of a selection
named!(where_clause<&[u8], ConditionExpression>,
    complete!(chain!(
        multispace? ~
        caseless_tag!("where") ~
        multispace ~
        cond: condition_expr,
        || { cond }
    ))
);

/// Parse rule for a SQL selection query.
/// TODO(malte): support nested queries as selection targets
named!(pub selection<&[u8], SelectStatement>,
    chain!(
        caseless_tag!("select") ~
        multispace ~
        distinct: opt!(caseless_tag!("distinct")) ~
        multispace? ~
        fields: field_expr ~
        delimited!(opt!(multispace), caseless_tag!("from"), opt!(multispace)) ~
        tables: table_list ~
        cond: opt!(where_clause) ~
        group_by: opt!(group_by_clause) ~
        order: opt!(order_clause) ~
        limit: opt!(limit_clause) ~
        statement_terminator,
        || {
            SelectStatement {
                tables: tables,
                distinct: distinct.is_some(),
                fields: fields,
                where_clause: cond,
                group_by: group_by,
                order: order,
                limit: limit,
            }
        }
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::{Column, FunctionExpression};
    use common::{FieldExpression, Operator};
    use condition::{ConditionBase, ConditionExpression, ConditionTree};
    use table::Table;

    fn columns(cols: &[&str]) -> Vec<Column> {
        cols.iter()
            .map(|c| Column::from(*c))
            .collect()
    }

    #[test]
    fn simple_select() {
        let qstring = "SELECT id, name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::Seq(columns(&["id", "name"])),
                       ..Default::default()
                   });
    }

    #[test]
    fn select_all() {
        let qstring = "SELECT * FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::All,
                       ..Default::default()
                   });
    }

    #[test]
    fn spaces_optional() {
        let qstring = "SELECT id,name FROM users;";

        let res = selection(qstring.as_bytes());
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::Seq(columns(&["id", "name"])),
                       ..Default::default()
                   });
    }

    #[test]
    fn case_sensitivity() {
        let qstring_lc = "select id, name from users;";
        let qstring_uc = "SELECT id, name FROM users;";

        assert_eq!(selection(qstring_lc.as_bytes()).unwrap(),
                   selection(qstring_uc.as_bytes()).unwrap());
    }

    #[test]
    fn termination() {
        let qstring_sem = "select id, name from users;";
        let qstring_linebreak = "select id, name from users\n";

        let r1 = selection(qstring_sem.as_bytes()).unwrap();
        let r2 = selection(qstring_linebreak.as_bytes()).unwrap();
        assert_eq!(r1, r2);
    }

    #[test]
    fn where_clause() {
        let qstring = "select * from ContactInfo where email=?;";

        let res = selection(qstring.as_bytes());

        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("email"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("ContactInfo")],
                       fields: FieldExpression::All,
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn limit_clause() {
        let qstring1 = "select * from users limit 10\n";
        let qstring2 = "select * from users limit 10 offset 10\n";

        let expected_lim1 = LimitClause {
            limit: 10,
            offset: 0,
        };
        let expected_lim2 = LimitClause {
            limit: 10,
            offset: 10,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        assert_eq!(res1.unwrap().1.limit, Some(expected_lim1));
        assert_eq!(res2.unwrap().1.limit, Some(expected_lim2));
    }

    #[test]
    fn order_clause() {
        let qstring1 = "select * from users order by name desc\n";
        let qstring2 = "select * from users order by name, age desc\n";
        let qstring3 = "select * from users order by name\n";

        let expected_ord1 = OrderClause {
            cols: columns(&["name"]),
            order: OrderType::OrderDescending,
        };
        let expected_ord2 = OrderClause {
            cols: columns(&["name", "age"]),
            order: OrderType::OrderDescending,
        };
        let expected_ord3 = OrderClause {
            cols: columns(&["name"]),
            order: OrderType::OrderAscending,
        };

        let res1 = selection(qstring1.as_bytes());
        let res2 = selection(qstring2.as_bytes());
        let res3 = selection(qstring3.as_bytes());
        assert_eq!(res1.unwrap().1.order, Some(expected_ord1));
        assert_eq!(res2.unwrap().1.order, Some(expected_ord2));
        assert_eq!(res3.unwrap().1.order, Some(expected_ord3));
    }

    #[test]
    fn table_alias() {
        let qstring1 = "select * from PaperTag as t;";
        // let qstring2 = "select * from PaperTag t;";

        let res1 = selection(qstring1.as_bytes());
        assert_eq!(res1.clone().unwrap().1,
                   SelectStatement {
                       tables: vec![Table {
                           name: String::from("PaperTag"),
                           alias: Some(String::from("t")),
                       }],
                       fields: FieldExpression::All,
                       ..Default::default()
                   });
        // let res2 = selection(qstring2.as_bytes());
        // assert_eq!(res1.unwrap().1, res2.unwrap().1);
    }

    #[test]
    fn distinct() {
        let qstring = "select distinct tag from PaperTag where paperId=?;";

        let res = selection(qstring.as_bytes());
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("PaperTag")],
                       distinct: true,
                       fields: FieldExpression::Seq(columns(&["tag"])),
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn simple_condition_expr() {
        let qstring = "select infoJson from PaperStorage where paperId=? and paperStorageId=?;";

        let res = selection(qstring.as_bytes());

        let left_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        })));
        let right_comp = Some(Box::new(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("paperStorageId"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        })));
        let expected_where_cond = Some(ConditionExpression::LogicalOp(ConditionTree {
            left: left_comp,
            right: right_comp,
            operator: Operator::And,
        }));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("PaperStorage")],
                       fields: FieldExpression::Seq(columns(&["infoJson"])),
                       where_clause: expected_where_cond,
                       ..Default::default()
                   });
    }

    #[test]
    fn where_and_limit_clauses() {
        let qstring = "select * from users where id = ? limit 10\n";
        let res = selection(qstring.as_bytes());

        let expected_lim = Some(LimitClause {
            limit: 10,
            offset: 0,
        });
        let expected_where_cond = Some(ConditionExpression::ComparisonOp(ConditionTree {
            left:
                Some(Box::new(ConditionExpression::Base(ConditionBase::Field(Column::from("id"))))),
            right: Some(Box::new(ConditionExpression::Base(ConditionBase::Placeholder))),
            operator: Operator::Equal,
        }));

        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("users")],
                       fields: FieldExpression::All,
                       where_clause: expected_where_cond,
                       limit: expected_lim,
                       ..Default::default()
                   });
    }

    #[test]
    fn aggregation_column() {
        let qstring = "SELECT max(addr_id) FROM address;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Max(FieldExpression::Seq(vec![Column::from("addr_id")]));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("address")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("anon_fn"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       ..Default::default()
                   });
    }

    #[test]
    fn aggregation_column_with_alias() {
        let qstring = "SELECT max(addr_id) AS max_addr FROM address;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Max(FieldExpression::Seq(vec![Column::from("addr_id")]));
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("address")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("max_addr"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       ..Default::default()
                   });
    }

    #[test]
    fn count_all() {
        let qstring = "SELECT COUNT(*) FROM votes GROUP BY aid;";

        let res = selection(qstring.as_bytes());
        let agg_expr = FunctionExpression::Count(FieldExpression::All);
        assert_eq!(res.unwrap().1,
                   SelectStatement {
                       tables: vec![Table::from("votes")],
                       fields: FieldExpression::Seq(vec![Column {
                                                             name: String::from("anon_fn"),
                                                             table: None,
                                                             function: Some(agg_expr),
                                                         }]),
                       group_by: Some(GroupByClause {
                           columns: vec![Column::from("aid")],
                           having: None,
                       }),
                       ..Default::default()
                   });
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Linaro LTD
// Copyright (c) 2017-2019 JUUL Labs
// Copyright (c) 2019 Arm Limited
//
// SPDX-License-Identifier: Apache-2.0

//! Interface wrappers to C API entering to the bootloader

use crate::area::AreaDesc;
use simflash::SimMultiFlash;
use crate::api;

/// Invoke the bootloader on this flash device.
pub fn boot_go(multiflash: &mut SimMultiFlash, areadesc: &AreaDesc,
               counter: Option<&mut i32>, catch_asserts: bool) -> (i32, u8) {
    for (&dev_id, flash) in multiflash.iter_mut() {
        api::set_flash(dev_id, flash);
    }
    let mut sim_ctx = api::CSimContext {
        flash_counter: match counter {
            None => 0,
            Some(ref c) => **c as libc::c_int
        },
        jumped: 0,
        c_asserts: 0,
        c_catch_asserts: if catch_asserts { 1 } else { 0 },
        boot_jmpbuf: [0; 16],
    };
    let result = unsafe {
        raw::invoke_boot_go(&mut sim_ctx as *mut _, &areadesc.get_c() as *const _) as i32
    };
    let asserts = sim_ctx.c_asserts;
    counter.map(|c| *c = sim_ctx.flash_counter);
    for &dev_id in multiflash.keys() {
        api::clear_flash(dev_id);
    }
    (result, asserts)
}

pub fn boot_trailer_sz(align: u32) -> u32 {
    unsafe { raw::boot_trailer_sz(align) }
}

pub fn boot_status_sz(align: u32) -> u32 {
    unsafe { raw::boot_status_sz(align) }
}

pub fn boot_magic_sz() -> usize {
    unsafe { raw::boot_magic_sz() as usize }
}

pub fn boot_max_align() -> usize {
    unsafe { raw::boot_max_align() as usize }
}

pub fn rsa_oaep_encrypt(pubkey: &[u8], seckey: &[u8]) -> Result<[u8; 256], &'static str> {
    unsafe {
        let mut encbuf: [u8; 256] = [0; 256];
        if raw::rsa_oaep_encrypt_(pubkey.as_ptr(), pubkey.len() as u32,
                                  seckey.as_ptr(), seckey.len() as u32,
                                  encbuf.as_mut_ptr()) == 0 {
            return Ok(encbuf);
        }
        return Err("Failed to encrypt buffer");
    }
}

pub fn kw_encrypt(kek: &[u8], seckey: &[u8]) -> Result<[u8; 24], &'static str> {
    unsafe {
        let mut encbuf = [0u8; 24];
        if raw::kw_encrypt_(kek.as_ptr(), seckey.as_ptr(), encbuf.as_mut_ptr()) == 0 {
            return Ok(encbuf);
        }
        return Err("Failed to encrypt buffer");
    }
}

mod raw {
    use crate::area::CAreaDesc;
    use crate::api::CSimContext;

    extern "C" {
        // This generates a warning about `CAreaDesc` not being foreign safe.  There doesn't appear to
        // be any way to get rid of this warning.  See https://github.com/rust-lang/rust/issues/34798
        // for information and tracking.
        pub fn invoke_boot_go(sim_ctx: *mut CSimContext, areadesc: *const CAreaDesc) -> libc::c_int;

        pub fn boot_trailer_sz(min_write_sz: u32) -> u32;
        pub fn boot_status_sz(min_write_sz: u32) -> u32;

        pub fn boot_magic_sz() -> u32;
        pub fn boot_max_align() -> u32;

        pub fn rsa_oaep_encrypt_(pubkey: *const u8, pubkey_len: libc::c_uint,
                                 seckey: *const u8, seckey_len: libc::c_uint,
                                 encbuf: *mut u8) -> libc::c_int;

        pub fn kw_encrypt_(kek: *const u8, seckey: *const u8,
                           encbuf: *mut u8) -> libc::c_int;
    }
}
<commit_msg>sim: Clarify condition<commit_after>// Copyright (c) 2017-2019 Linaro LTD
// Copyright (c) 2017-2019 JUUL Labs
// Copyright (c) 2019 Arm Limited
//
// SPDX-License-Identifier: Apache-2.0

//! Interface wrappers to C API entering to the bootloader

use crate::area::AreaDesc;
use simflash::SimMultiFlash;
use crate::api;

/// Invoke the bootloader on this flash device.
pub fn boot_go(multiflash: &mut SimMultiFlash, areadesc: &AreaDesc,
               counter: Option<&mut i32>, catch_asserts: bool) -> (i32, u8) {
    for (&dev_id, flash) in multiflash.iter_mut() {
        api::set_flash(dev_id, flash);
    }
    let mut sim_ctx = api::CSimContext {
        flash_counter: match counter {
            None => 0,
            Some(ref c) => **c as libc::c_int
        },
        jumped: 0,
        c_asserts: 0,
        c_catch_asserts: if catch_asserts { 1 } else { 0 },
        boot_jmpbuf: [0; 16],
    };
    let result = unsafe {
        raw::invoke_boot_go(&mut sim_ctx as *mut _, &areadesc.get_c() as *const _) as i32
    };
    let asserts = sim_ctx.c_asserts;
    if let Some(c) = counter {
        *c = sim_ctx.flash_counter;
    }
    for &dev_id in multiflash.keys() {
        api::clear_flash(dev_id);
    }
    (result, asserts)
}

pub fn boot_trailer_sz(align: u32) -> u32 {
    unsafe { raw::boot_trailer_sz(align) }
}

pub fn boot_status_sz(align: u32) -> u32 {
    unsafe { raw::boot_status_sz(align) }
}

pub fn boot_magic_sz() -> usize {
    unsafe { raw::boot_magic_sz() as usize }
}

pub fn boot_max_align() -> usize {
    unsafe { raw::boot_max_align() as usize }
}

pub fn rsa_oaep_encrypt(pubkey: &[u8], seckey: &[u8]) -> Result<[u8; 256], &'static str> {
    unsafe {
        let mut encbuf: [u8; 256] = [0; 256];
        if raw::rsa_oaep_encrypt_(pubkey.as_ptr(), pubkey.len() as u32,
                                  seckey.as_ptr(), seckey.len() as u32,
                                  encbuf.as_mut_ptr()) == 0 {
            return Ok(encbuf);
        }
        return Err("Failed to encrypt buffer");
    }
}

pub fn kw_encrypt(kek: &[u8], seckey: &[u8]) -> Result<[u8; 24], &'static str> {
    unsafe {
        let mut encbuf = [0u8; 24];
        if raw::kw_encrypt_(kek.as_ptr(), seckey.as_ptr(), encbuf.as_mut_ptr()) == 0 {
            return Ok(encbuf);
        }
        return Err("Failed to encrypt buffer");
    }
}

mod raw {
    use crate::area::CAreaDesc;
    use crate::api::CSimContext;

    extern "C" {
        // This generates a warning about `CAreaDesc` not being foreign safe.  There doesn't appear to
        // be any way to get rid of this warning.  See https://github.com/rust-lang/rust/issues/34798
        // for information and tracking.
        pub fn invoke_boot_go(sim_ctx: *mut CSimContext, areadesc: *const CAreaDesc) -> libc::c_int;

        pub fn boot_trailer_sz(min_write_sz: u32) -> u32;
        pub fn boot_status_sz(min_write_sz: u32) -> u32;

        pub fn boot_magic_sz() -> u32;
        pub fn boot_max_align() -> u32;

        pub fn rsa_oaep_encrypt_(pubkey: *const u8, pubkey_len: libc::c_uint,
                                 seckey: *const u8, seckey_len: libc::c_uint,
                                 encbuf: *mut u8) -> libc::c_int;

        pub fn kw_encrypt_(kek: *const u8, seckey: *const u8,
                           encbuf: *mut u8) -> libc::c_int;
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]
extern crate hyper;

use std::io::Read;

use rustc_serialize::*;

use hyper::{Get, Post};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header::{ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

use payment_detection::PaymentDetection;
use blockchain_info::*;
use qr;

macro_rules! try_return(
	($e:expr) => {{
		match $e {
			Ok(v) => v,
			Err(e) => { println!("Error: {}", e); return; }
		}
	}}
);

pub fn gen_resp_json(error: i64) -> Vec<u8> {
	let payment_response = PaymentResponse::new(&error);
	let payment_response_json = json::encode(&payment_response).unwrap();

	payment_response_json.as_bytes().to_vec()
}

pub fn routes(req: Request, res: Response) {
	let handler: fn(Request, Response) = match req.uri {
		AbsolutePath(ref path) => {
			let url_path = path.split("?").next().unwrap();

			match (&req.method, url_path) {
				(&Post, "/detect_payment") => detect_payment,
				(&Get, "/qr_code") => qr_code,
				_ => not_found
			}
		},
		_ => not_found

	};

	handler(req, res);
}

pub fn not_found(_: Request, mut res: Response) {
	*res.status_mut() = hyper::NotFound;

}

pub fn detect_payment(mut req: Request, mut res: Response) {
	res.headers_mut().set(
		ContentType(
			Mime(
				TopLevel::Application,
				SubLevel::Json,
				vec![(Attr::Charset, Value::Utf8)]
			)
		)
	);

	*res.status_mut() = hyper::Ok;

	let mut body = String::new();
	let _ = req.read_to_string(&mut body);
	println!("Request body: {:?}", body);

	let payment_request: PaymentRequest = match json::decode(&body) {
		Ok(pr) => pr,
		Err(e) => {
			try_return!(res.send(&gen_resp_json(-1)));
			panic!("JSON Decoder: {}", e)
		}
	};

	let blockchain_info = BlockchainInfo::new(&payment_request.address, payment_request.amount);

	match blockchain_info.wait() {
		Ok(_) => {
			println!("Payment received!");
			try_return!(res.send(&gen_resp_json(0)));
		}
		Err(e) => {
			println!("Payment not received in time {:?}", e);
			try_return!(res.send(&gen_resp_json(-1)));
		}
	};
}

pub fn qr_code(req: Request, mut res: Response) {
	res.headers_mut().set(
		ContentType(
			Mime(
				TopLevel::Image,
				SubLevel::Png,
				vec![(Attr::Charset, Value::Utf8)]
			)
		)
	);

	*res.status_mut() = hyper::Ok;

	let uri: &str = match req.uri {
		AbsolutePath(ref uri) => uri,
		_ => {
			*res.status_mut() = hyper::NotFound;
			return
		}
	};

	let re_btc_amount = regex!(r"[\?&]btc_amount=([0-9\.]+)");
	let re_btc_receiver_address = regex!(r"[\?|&]btc_receiver_address=([0-9a-zA-Z]{26,34})");

	let btc_amount_captures = re_btc_amount.captures(uri).unwrap();
	let btc_amount = btc_amount_captures.at(1).unwrap();

	let btc_receiver_address_captures = re_btc_receiver_address.captures(uri).unwrap();
	let btc_receiver_address = btc_receiver_address_captures.at(1).unwrap();

	let bitcoin_payment_request = format!("bitcoin:{}?amount={}", btc_receiver_address, btc_amount);

	let png_data = qr::create(&bitcoin_payment_request);

	try_return!(res.send(&png_data));
}

#[derive(RustcDecodable)]
pub struct PaymentRequest {
	pub address: String,
	pub amount: u64,
}

#[derive(RustcEncodable)]
pub struct PaymentResponse<'a> {
	pub error: &'a i64
}

impl<'a> PaymentResponse<'a> {
	pub fn new(error: &'a i64) -> PaymentResponse {
		PaymentResponse {
			error: error
		}
	}
}
<commit_msg>refactored detect_payment http handler for better debugging<commit_after>#![deny(warnings)]
extern crate hyper;

use hyper::Get;
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header::{ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

use payment_detection::PaymentDetection;
use blockchain_info::*;
use qr;

macro_rules! try_return(
	($e:expr) => {{
		match $e {
			Ok(v) => v,
			Err(e) => { println!("Error: {}", e); return; }
		}
	}}
);

pub fn routes(req: Request, res: Response) {
	let handler: fn(Request, Response) = match req.uri {
		AbsolutePath(ref path) => {
			let url_path = path.split("?").next().unwrap();

			match (&req.method, url_path) {
				(&Get, "/detect_payment") => detect_payment,
				(&Get, "/qr_code") => qr_code,
				_ => not_found
			}
		},
		_ => not_found

	};

	handler(req, res);
}

pub fn not_found(_: Request, mut res: Response) {
	*res.status_mut() = hyper::NotFound;

}

pub fn detect_payment(req: Request, mut res: Response) {
	res.headers_mut().set(
		ContentType(
			Mime(
				TopLevel::Text,
				SubLevel::Plain,
				vec![(Attr::Charset, Value::Utf8)]
			)
		)
	);

	let uri: &str = match req.uri {
		AbsolutePath(ref uri) => uri,
		_ => {
			*res.status_mut() = hyper::NotFound;
			return
		}
	};

	let re_btc_amount = regex!(r"[\?&]btc_amount=([0-9\.]+)");
	let re_btc_receiver_address = regex!(r"[\?|&]btc_receiver_address=([0-9a-zA-Z]{26,34})");

	let btc_amount_captures = re_btc_amount.captures(uri).unwrap();
	let btc_amount_str = btc_amount_captures.at(1).unwrap();

	let btc_receiver_address_captures = re_btc_receiver_address.captures(uri).unwrap();
	let btc_receiver_address = btc_receiver_address_captures.at(1).unwrap();

	let btc_amount = btc_amount_str.parse::<f64>().unwrap();
	let satoshi_amount = btc_amount * 1000.0 * 1000.0 * 100.0;
	let blockchain_info = BlockchainInfo::new(btc_receiver_address, satoshi_amount as u64);

	*res.status_mut() = hyper::Ok;

	match blockchain_info.wait() {
		Ok(_) => {
			try_return!(res.send(&"payment received".as_bytes().to_vec()));
		}
		Err(e) => {
			try_return!(res.send(&format!("payment not received in time {}", e).as_bytes().to_vec()));
		}
	};
}

pub fn qr_code(req: Request, mut res: Response) {
	res.headers_mut().set(
		ContentType(
			Mime(
				TopLevel::Image,
				SubLevel::Png,
				vec![(Attr::Charset, Value::Utf8)]
			)
		)
	);

	let uri: &str = match req.uri {
		AbsolutePath(ref uri) => uri,
		_ => {
			*res.status_mut() = hyper::NotFound;
			return
		}
	};

	let re_btc_amount = regex!(r"[\?&]btc_amount=([0-9\.]+)");
	let re_btc_receiver_address = regex!(r"[\?|&]btc_receiver_address=([0-9a-zA-Z]{26,34})");

	let btc_amount_captures = re_btc_amount.captures(uri).unwrap();
	let btc_amount = btc_amount_captures.at(1).unwrap();

	let btc_receiver_address_captures = re_btc_receiver_address.captures(uri).unwrap();
	let btc_receiver_address = btc_receiver_address_captures.at(1).unwrap();

	let bitcoin_payment_request = format!("bitcoin:{}?amount={}", btc_receiver_address, btc_amount);

	let png_data = qr::create(&bitcoin_payment_request);

	*res.status_mut() = hyper::Ok;

	try_return!(res.send(&png_data));
}
<|endoftext|>


<commit_before>extern crate rand;

use self::rand::{thread_rng,Rng};

use std::collections::HashMap;
use std::sync::{Arc,RwLock,Mutex,mpsc};

use super::libcoinche::{bid,cards,pos,game};

pub enum ServerError {
    BadPlayerId,
    BadEventId,
    PlayInAuction,
    BidInGame,
    Bid(bid::BidError),
    Play(game::PlayError),
}

pub enum Action {
    Bid(bid::Contract),
    Coinche,
    Pass,
    Play(cards::Card),
}

#[derive(Clone)]
pub enum PlayerEvent {
    Bidded(bid::Contract),
    Coinched,
    Passed,
    CardPlayed(cards::Card),
}

// Player just joined a new party. He's given a player id, and his position.
pub struct NewPartyInfo {
    pub player_id: u32,
    pub player_pos: pos::PlayerPos,
}

// Represents an event that can happen during the game.
#[derive(Clone)]
pub enum EventType {
    // The party is cancelled. Contains an optional explanation.
    PartyCancelled(String),

    // A player did something!
    FromPlayer(pos::PlayerPos, PlayerEvent),

    // Bid over: contains the contract and the author
    BidOver(bid::Contract),
    // The bid was cancelled, probably because no one bidded anything.
    // A new game is probably on its way.
    BidCancelled,

    // Trick over: contains the winner
    TrickOver(pos::PlayerPos),

    // New game: contains the first player, and the player's hand
    NewGame(pos::PlayerPos, [cards::Hand;4]),

    // Game over: contains scores
    GameOver([i32;2], pos::Team, [i32;2]),
    GameCancelled,
}

#[derive(Clone)]
pub struct Event {
    pub event: EventType,
    pub id: usize,
}

pub struct Order {
    pub author: pos::PlayerPos,
    pub action: Action
}

pub struct Server {
    party_list: RwLock<PartyList>,

    waiting_list: Mutex<Vec<mpsc::Sender<NewPartyInfo>>>,
}

pub enum Game {
    Bidding(bid::Auction),
    Playing(game::GameState),
}

fn make_game(first: pos::PlayerPos) -> (bid::Auction, EventType) {
    let auction = bid::new_auction(first);
    let hands = auction.hands();

    let event = EventType::NewGame(first, hands);

    (auction,event)
}

pub struct Party {
    game: Game,
    first: pos::PlayerPos,

    scores: [i32; 2],

    events: Vec<EventType>,
    observers: Vec<mpsc::Sender<Event>>,
}

fn new_party(first: pos::PlayerPos) -> Party {
    let (auction,event) = make_game(first);
    Party {
        first: first,
        game: Game::Bidding(auction),
        scores: [0;2],
        events: vec![event],
        observers: Vec::new(),
    }
}

impl Party {
    fn add_event(&mut self, event: EventType) -> Event {
        let ev = Event{
            event: event.clone(),
            id: self.events.len(),
        };
        for sender in self.observers.iter() {
            // TODO: handle cancelled wait?
            sender.send(ev.clone()).unwrap();
        }
        self.observers.clear();
        self.events.push(event);

        ev
    }

    fn new_game(&mut self) {
        // TODO: Maybe keep the current game in the history?

        let (auction, event) = make_game(self.first);

        self.first = self.first.next();
        self.game = Game::Bidding(auction);
        self.add_event(event);
    }

    fn bid(&mut self, pos: pos::PlayerPos, contract: bid::Contract) -> Result<Event,ServerError> {
        let state = match &mut self.game {
            &mut Game::Bidding(ref mut auction) => {
                match auction.bid(contract.clone()) {
                    Ok(state) => state,
                    Err(err) => return Err(ServerError::Bid(err)),
                }

            },
            &mut Game::Playing(_) => return Err(ServerError::BidInGame),
        };

        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::Bidded(contract.clone())));
        match state {
            bid::AuctionState::Over => self.complete_auction(),
            _ => (),
        }

        Ok(main_event)
    }

    fn pass(&mut self, pos: pos::PlayerPos) -> Result<Event,ServerError> {
        let state = match &mut self.game {
            &mut Game::Bidding(ref mut auction) => auction.pass(),
            &mut Game::Playing(_) => return Err(ServerError::BidInGame),
        };

        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::Passed));
        match state {
            bid::AuctionState::Over => self.complete_auction(),
            bid::AuctionState::Cancelled => {
                self.add_event(EventType::GameCancelled);
                self.new_game();
            },
            _ => (),
        }

        Ok(main_event)
    }

    fn complete_auction(&mut self) {
        let game = match &mut self.game {
            &mut Game::Playing(_) => unreachable!(),
            &mut Game::Bidding(ref mut auction) => {
                match auction.complete() {
                    Ok(game) => game,
                    Err(err) => panic!(err),
                }
            }
        };

        self.add_event(EventType::BidOver(game.contract().clone()));

        self.game = Game::Playing(game);
    }

    fn play_card(&mut self, pos: pos::PlayerPos, card: cards::Card) -> Result<Event,ServerError> {
        let result = match &mut self.game {
            &mut Game::Playing(ref mut game) => {
                match game.play_card(pos, card) {
                    Ok(result) => result,
                    Err(err) => return Err(ServerError::Play(err)),
                }
            },
            &mut Game::Bidding(_) => {
                // Bad time for a card play!
                return Err(ServerError::PlayInAuction);
            },
        };
        // This is the main event we want to send.
        // TODO: Batch event dispatch, and send all those together.
        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::CardPlayed(card)));
        match result {
            game::TrickResult::Nothing => (),
            game::TrickResult::TrickOver(winner, game_result) => {
                self.add_event(EventType::TrickOver(winner));
                match game_result {
                    game::GameResult::Nothing => (),
                    game::GameResult::GameOver(points, winners, scores) => {
                        for i in 0..2 { self.scores[i] += scores[i]; }
                        let total_scores = self.scores;
                        self.add_event(EventType::GameOver(points, winners, total_scores));
                        self.new_game();
                    }
                }
            },
        }

        Ok(main_event)
    }
}

pub struct PlayerInfo {
    pub party: Arc<RwLock<Party>>,
    pub pos: pos::PlayerPos,
}

pub struct PartyList {
    pub player_map: HashMap<u32,PlayerInfo>,
}

impl PartyList {
    fn make_ids(&self) -> [u32; 4] {
        // Expect self.player_map to be locked
        let mut result = [0;4];

        for i in 0..4 {
            loop {
                let id = thread_rng().next_u32();
                if self.player_map.contains_key(&id) {
                    continue;
                }
                let mut ok = true;
                for j in 0..i {
                    if result[j] == id {
                        ok = false;
                        break;
                    }
                }
                if !ok {
                    continue;
                }

                result[i] = id;
            }
        }

        result
    }
}

enum WaitResult {
    Ready(Event),
    Waiting(mpsc::Receiver<Event>),
}

enum JoinResult {
    Ready(NewPartyInfo),
    Waiting(mpsc::Receiver<NewPartyInfo>),
}

impl Server {
    pub fn join(&self) -> Option<NewPartyInfo> {
        match self.get_join_result() {
            // TODO: add a timeout (max: 20s)
            // TODO: handle cancelled join?
            JoinResult::Ready(info) => Some(info),
            JoinResult::Waiting(rx) => Some(rx.recv().unwrap()),
        }
    }

    fn get_join_result(&self) -> JoinResult {
        let mut waiters = self.waiting_list.lock().unwrap();
        if waiters.len() >= 3 {
            // It's a PARTEY!
            let info = self.make_party([
                                       waiters.pop().unwrap(),
                                       waiters.pop().unwrap(),
                                       waiters.pop().unwrap(),
            ]);
            return JoinResult::Ready(info);
        } else {
            let (tx,rx) = mpsc::channel();
            waiters.push(tx);
            return JoinResult::Waiting(rx);
        }
    }

    fn make_party(&self, others: [mpsc::Sender<NewPartyInfo>; 3]) -> NewPartyInfo {
        let mut list = self.party_list.write().unwrap();

        // Generate 4 new IDS
        let ids = list.make_ids();

        let party = Arc::new(RwLock::new(new_party(pos::P0)));
        // Kickstart it with a new game!

        // Prepare the players info
        for i in 0..4 {
            list.player_map.insert(ids[i], PlayerInfo {
                party: party.clone(),
                pos: pos::PlayerPos(i),
            });
        }

        // Tell everyone. They'll love it.
        // TODO: handle cancelled channels (?)
        for i in 0..3 {
            others[i].send(NewPartyInfo{
                player_id: ids[i],
                player_pos: pos::PlayerPos(i),
            }).unwrap();
        }

        // Even you, weird 4th dude.
        NewPartyInfo{
            player_id: ids[3],
            player_pos: pos::P3,
        }
    }

    // Play a card in the current game
    pub fn play_card(&self, player_id: u32, card: cards::Card) -> Result<Event,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.play_card(info.pos, card)

    }

    pub fn bid(&self, player_id: u32, contract: bid::Contract) -> Result<Event,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.bid(info.pos, contract)
    }

    pub fn pass(&self, player_id: u32) -> Result<Event, ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.pass(info.pos)
    }

    // TODO: add coinche()

    // TODO: add getter methods: get_hand, ...

    // Waits until the given event_id happens
    pub fn wait(&self, player_id: u32, event_id: usize) -> Result<Event,ServerError> {
        let res = self.get_wait_result(player_id, event_id);

        // TODO: add a timeout (~15s?)

        match res {
            Err(err) => Err(err),
            Ok(WaitResult::Ready(event)) => Ok(event),
            // TODO: handle case where the wait is cancelled
            // (don't unwrap, return an error instead?)
            Ok(WaitResult::Waiting(rx)) => Ok(rx.recv().unwrap()),
        }
    }

    // Check if the event ID is already available. If not, returns a channel that will produce it one
    // day, so that we don't keep the locks while waiting.
    fn get_wait_result(&self, player_id: u32, event_id: usize) -> Result<WaitResult,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();

        if party.events.len() > event_id {
            return Ok(WaitResult::Ready(Event {
                event: party.events[event_id].clone(),
                id: event_id,
            }));
        } else if event_id > party.events.len() {
            // We are too ambitious! One event at a time!
            return Err(ServerError::BadEventId);
        }

        let (tx, rx) = mpsc::channel();
        party.observers.push(tx);

        Ok(WaitResult::Waiting(rx))
    }
}

<commit_msg>Add coinche method to server<commit_after>extern crate rand;

use self::rand::{thread_rng,Rng};

use std::collections::HashMap;
use std::sync::{Arc,RwLock,Mutex,mpsc};

use super::libcoinche::{bid,cards,pos,game};

pub enum ServerError {
    BadPlayerId,
    BadEventId,
    PlayInAuction,
    BidInGame,
    Bid(bid::BidError),
    Play(game::PlayError),
}

pub enum Action {
    Bid(bid::Contract),
    Coinche,
    Pass,
    Play(cards::Card),
}

#[derive(Clone)]
pub enum PlayerEvent {
    Bidded(bid::Contract),
    Coinched,
    Passed,
    CardPlayed(cards::Card),
}

// Player just joined a new party. He's given a player id, and his position.
pub struct NewPartyInfo {
    pub player_id: u32,
    pub player_pos: pos::PlayerPos,
}

// Represents an event that can happen during the game.
#[derive(Clone)]
pub enum EventType {
    // The party is cancelled. Contains an optional explanation.
    PartyCancelled(String),

    // A player did something!
    FromPlayer(pos::PlayerPos, PlayerEvent),

    // Bid over: contains the contract and the author
    BidOver(bid::Contract),
    // The bid was cancelled, probably because no one bidded anything.
    // A new game is probably on its way.
    BidCancelled,

    // Trick over: contains the winner
    TrickOver(pos::PlayerPos),

    // New game: contains the first player, and the player's hand
    NewGame(pos::PlayerPos, [cards::Hand;4]),

    // Game over: contains scores
    GameOver([i32;2], pos::Team, [i32;2]),
    GameCancelled,
}

#[derive(Clone)]
pub struct Event {
    pub event: EventType,
    pub id: usize,
}

pub struct Order {
    pub author: pos::PlayerPos,
    pub action: Action
}

pub struct Server {
    party_list: RwLock<PartyList>,

    waiting_list: Mutex<Vec<mpsc::Sender<NewPartyInfo>>>,
}

pub enum Game {
    Bidding(bid::Auction),
    Playing(game::GameState),
}

fn make_game(first: pos::PlayerPos) -> (bid::Auction, EventType) {
    let auction = bid::new_auction(first);
    let hands = auction.hands();

    let event = EventType::NewGame(first, hands);

    (auction,event)
}

pub struct Party {
    game: Game,
    first: pos::PlayerPos,

    scores: [i32; 2],

    events: Vec<EventType>,
    observers: Vec<mpsc::Sender<Event>>,
}

fn new_party(first: pos::PlayerPos) -> Party {
    let (auction,event) = make_game(first);
    Party {
        first: first,
        game: Game::Bidding(auction),
        scores: [0;2],
        events: vec![event],
        observers: Vec::new(),
    }
}

impl Party {
    fn add_event(&mut self, event: EventType) -> Event {
        let ev = Event{
            event: event.clone(),
            id: self.events.len(),
        };
        for sender in self.observers.iter() {
            // TODO: handle cancelled wait?
            sender.send(ev.clone()).unwrap();
        }
        self.observers.clear();
        self.events.push(event);

        ev
    }

    fn new_game(&mut self) {
        // TODO: Maybe keep the current game in the history?

        let (auction, event) = make_game(self.first);

        self.first = self.first.next();
        self.game = Game::Bidding(auction);
        self.add_event(event);
    }

    fn bid(&mut self, pos: pos::PlayerPos, contract: bid::Contract) -> Result<Event,ServerError> {
        let state = match &mut self.game {
            &mut Game::Bidding(ref mut auction) => {
                match auction.bid(contract.clone()) {
                    Ok(state) => state,
                    Err(err) => return Err(ServerError::Bid(err)),
                }

            },
            &mut Game::Playing(_) => return Err(ServerError::BidInGame),
        };

        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::Bidded(contract.clone())));
        match state {
            bid::AuctionState::Over => self.complete_auction(),
            _ => (),
        }

        Ok(main_event)
    }

    fn pass(&mut self, pos: pos::PlayerPos) -> Result<Event,ServerError> {
        let state = match &mut self.game {
            &mut Game::Bidding(ref mut auction) => auction.pass(),
            &mut Game::Playing(_) => return Err(ServerError::BidInGame),
        };

        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::Passed));
        match state {
            bid::AuctionState::Over => self.complete_auction(),
            bid::AuctionState::Cancelled => {
                self.add_event(EventType::GameCancelled);
                self.new_game();
            },
            _ => (),
        }

        Ok(main_event)
    }

    fn coinche(&mut self, pos: pos::PlayerPos) -> Result<Event, ServerError> {
        let state = match &mut self.game {
            &mut Game::Bidding(ref mut auction) => match auction.coinche() {
                Ok(state) => state,
                Err(err) => return Err(ServerError::Bid(err)),
            },
            &mut Game::Playing(_) => return Err(ServerError::BidInGame),
        };

        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::Coinched));
        match state {
            bid::AuctionState::Over => self.complete_auction(),
            _ => (),
        }

        Ok(main_event)
    }

    fn complete_auction(&mut self) {
        let game = match &mut self.game {
            &mut Game::Playing(_) => unreachable!(),
            &mut Game::Bidding(ref mut auction) => {
                match auction.complete() {
                    Ok(game) => game,
                    Err(err) => panic!(err),
                }
            }
        };

        self.add_event(EventType::BidOver(game.contract().clone()));

        self.game = Game::Playing(game);
    }

    fn play_card(&mut self, pos: pos::PlayerPos, card: cards::Card) -> Result<Event,ServerError> {
        let result = match &mut self.game {
            &mut Game::Playing(ref mut game) => {
                match game.play_card(pos, card) {
                    Ok(result) => result,
                    Err(err) => return Err(ServerError::Play(err)),
                }
            },
            &mut Game::Bidding(_) => {
                // Bad time for a card play!
                return Err(ServerError::PlayInAuction);
            },
        };
        // This is the main event we want to send.
        // TODO: Batch event dispatch, and send all those together.
        let main_event = self.add_event(EventType::FromPlayer(pos, PlayerEvent::CardPlayed(card)));
        match result {
            game::TrickResult::Nothing => (),
            game::TrickResult::TrickOver(winner, game_result) => {
                self.add_event(EventType::TrickOver(winner));
                match game_result {
                    game::GameResult::Nothing => (),
                    game::GameResult::GameOver(points, winners, scores) => {
                        for i in 0..2 { self.scores[i] += scores[i]; }
                        let total_scores = self.scores;
                        self.add_event(EventType::GameOver(points, winners, total_scores));
                        self.new_game();
                    }
                }
            },
        }

        Ok(main_event)
    }
}

pub struct PlayerInfo {
    pub party: Arc<RwLock<Party>>,
    pub pos: pos::PlayerPos,
}

pub struct PartyList {
    pub player_map: HashMap<u32,PlayerInfo>,
}

impl PartyList {
    fn make_ids(&self) -> [u32; 4] {
        // Expect self.player_map to be locked
        let mut result = [0;4];

        for i in 0..4 {
            loop {
                let id = thread_rng().next_u32();
                if self.player_map.contains_key(&id) {
                    continue;
                }
                let mut ok = true;
                for j in 0..i {
                    if result[j] == id {
                        ok = false;
                        break;
                    }
                }
                if !ok {
                    continue;
                }

                result[i] = id;
            }
        }

        result
    }
}

enum WaitResult {
    Ready(Event),
    Waiting(mpsc::Receiver<Event>),
}

enum JoinResult {
    Ready(NewPartyInfo),
    Waiting(mpsc::Receiver<NewPartyInfo>),
}

impl Server {
    pub fn join(&self) -> Option<NewPartyInfo> {
        match self.get_join_result() {
            // TODO: add a timeout (max: 20s)
            // TODO: handle cancelled join?
            JoinResult::Ready(info) => Some(info),
            JoinResult::Waiting(rx) => Some(rx.recv().unwrap()),
        }
    }

    fn get_join_result(&self) -> JoinResult {
        let mut waiters = self.waiting_list.lock().unwrap();
        if waiters.len() >= 3 {
            // It's a PARTEY!
            let info = self.make_party([
                                       waiters.pop().unwrap(),
                                       waiters.pop().unwrap(),
                                       waiters.pop().unwrap(),
            ]);
            return JoinResult::Ready(info);
        } else {
            let (tx,rx) = mpsc::channel();
            waiters.push(tx);
            return JoinResult::Waiting(rx);
        }
    }

    fn make_party(&self, others: [mpsc::Sender<NewPartyInfo>; 3]) -> NewPartyInfo {
        let mut list = self.party_list.write().unwrap();

        // Generate 4 new IDS
        let ids = list.make_ids();

        let party = Arc::new(RwLock::new(new_party(pos::P0)));
        // Kickstart it with a new game!

        // Prepare the players info
        for i in 0..4 {
            list.player_map.insert(ids[i], PlayerInfo {
                party: party.clone(),
                pos: pos::PlayerPos(i),
            });
        }

        // Tell everyone. They'll love it.
        // TODO: handle cancelled channels (?)
        for i in 0..3 {
            others[i].send(NewPartyInfo{
                player_id: ids[i],
                player_pos: pos::PlayerPos(i),
            }).unwrap();
        }

        // Even you, weird 4th dude.
        NewPartyInfo{
            player_id: ids[3],
            player_pos: pos::P3,
        }
    }

    // Play a card in the current game
    pub fn play_card(&self, player_id: u32, card: cards::Card) -> Result<Event,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.play_card(info.pos, card)

    }

    pub fn bid(&self, player_id: u32, contract: bid::Contract) -> Result<Event,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.bid(info.pos, contract)
    }

    pub fn pass(&self, player_id: u32) -> Result<Event, ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.pass(info.pos)
    }

    pub fn coinche(&self, player_id: u32) -> Result<Event, ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();
        party.coinche(info.pos)
    }

    // TODO: add getter methods: get_hand, ...

    // Waits until the given event_id happens
    pub fn wait(&self, player_id: u32, event_id: usize) -> Result<Event,ServerError> {
        let res = self.get_wait_result(player_id, event_id);

        // TODO: add a timeout (~15s?)

        match res {
            Err(err) => Err(err),
            Ok(WaitResult::Ready(event)) => Ok(event),
            // TODO: handle case where the wait is cancelled
            // (don't unwrap, return an error instead?)
            Ok(WaitResult::Waiting(rx)) => Ok(rx.recv().unwrap()),
        }
    }

    // Check if the event ID is already available. If not, returns a channel that will produce it one
    // day, so that we don't keep the locks while waiting.
    fn get_wait_result(&self, player_id: u32, event_id: usize) -> Result<WaitResult,ServerError> {
        let list = self.party_list.read().unwrap();

        let info = match list.player_map.get(&player_id) {
            Some(info) => info,
            None => return Err(ServerError::BadPlayerId),
        };

        let mut party = info.party.write().unwrap();

        if party.events.len() > event_id {
            return Ok(WaitResult::Ready(Event {
                event: party.events[event_id].clone(),
                id: event_id,
            }));
        } else if event_id > party.events.len() {
            // We are too ambitious! One event at a time!
            return Err(ServerError::BadEventId);
        }

        let (tx, rx) = mpsc::channel();
        party.observers.push(tx);

        Ok(WaitResult::Waiting(rx))
    }
}

<|endoftext|>


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

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on. This information is used internally to calculate the correct memory
//! locations for the various BCM283x peripherals.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// Based on the output of `/proc/cpuinfo`, it wasn't possible to identify the Raspberry Pi
/// model.
        UnknownModel { description("unknown Raspberry Pi model") }
/// Can't access `/proc/cpuinfo`.
///
/// Unable to read the contents of `/proc/cpuinfo`. This could be an issue with permissions, or
/// a Linux distribution is used that doesn't provide access to this virtual file.
        CantAccessProcCpuInfo { description("can't access /proc/cpuinfo") }
    }
}

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

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` parses the contents of `/proc/cpuinfo` to identify the Raspberry
    /// Pi's model and SoC.
    pub fn new() -> Result<DeviceInfo> {
        // Parse /proc/cpuinfo to extract hardware/revision
        let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
            Err(_) => return Err(Error::CantAccessProcCpuInfo),
            Ok(file) => file,
        });

        let mut hardware: String = String::new();
        let mut revision: String = String::new();
        for line_result in proc_cpuinfo.lines() {
            if let Ok(line) = line_result {
                if line.starts_with("Hardware\t: ") {
                    hardware = String::from(&line[11..]);
                } else if line.starts_with("Revision\t: ") {
                    revision = String::from(&line[11..]).to_lowercase();
                }
            }
        }

        // Return an error if we don't recognize the SoC. This check is
        // done to prevent accidentally identifying a non-Pi SBC as a Pi
        // solely based on the revision field.
        match &hardware[..] {
            "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
            | "BCM2837B0" => {}
            _ => return Err(Error::UnknownModel),
        }

        let model = if (revision.len() == 4) || (revision.len() == 8) {
            // Older revisions are 4 characters long, or 8 if they've been over-volted
            match &revision[revision.len() - 4..] {
                "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
                "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
                "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
                "0012" => Model::RaspberryPiAPlus,
                "0010" | "0013" => Model::RaspberryPiBPlus,
                "0011" | "0014" => Model::RaspberryPiComputeModule,
                _ => return Err(Error::UnknownModel),
            }
        } else if revision.len() >= 6 {
            // Newer revisions consist of at least 6 characters
            match &revision[revision.len() - 3..revision.len() - 1] {
                "00" => Model::RaspberryPiA,
                "01" => Model::RaspberryPiBRev2,
                "02" => Model::RaspberryPiAPlus,
                "03" => Model::RaspberryPiBPlus,
                "04" => Model::RaspberryPi2B,
                "06" => Model::RaspberryPiComputeModule,
                "08" => Model::RaspberryPi3B,
                "09" => Model::RaspberryPiZero,
                "0a" => Model::RaspberryPiComputeModule3,
                "0c" => Model::RaspberryPiZeroW,
                "0d" => Model::RaspberryPi3BPlus,
                _ => return Err(Error::UnknownModel),
            }
        } else {
            return Err(Error::UnknownModel);
        };

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

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

//! Raspberry Pi system-related tools.
//!
//! Use [`DeviceInfo`] to identify what Raspberry Pi model and SoC the software is
//! running on. This information is used internally to calculate the correct memory
//! locations for the various BCM283x peripherals.
//!
//! [`DeviceInfo`]: struct.DeviceInfo.html

use std::fmt;
use std::fs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
const GPIO_OFFSET: u32 = 0x20_0000;

quick_error! {
/// Errors that can occur when trying to identify the Raspberry Pi hardware.
    #[derive(Debug)]
    pub enum Error {
/// Unknown model.
///
/// `DeviceInfo` was unable to identify the Raspberry Pi model based on the
/// contents of `/proc/cpuinfo`, `/sys/firmware/devicetree/base/compatible`
/// and `/sys/firmware/devicetree/base/model`.
        UnknownModel { description("unknown Raspberry Pi model") }
    }
}

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

/// Identifiable Raspberry Pi models.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Model {
    RaspberryPiA,
    RaspberryPiAPlus,
    RaspberryPiB,
    RaspberryPiBRev1,
    RaspberryPiBRev2,
    RaspberryPiBPlus,
    RaspberryPi2B,
    RaspberryPi3B,
    RaspberryPi3BPlus,
    RaspberryPiComputeModule,
    RaspberryPiComputeModule3,
    RaspberryPiZero,
    RaspberryPiZeroW,
}

impl fmt::Display for Model {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Model::RaspberryPiA => write!(f, "Raspberry Pi A"),
            Model::RaspberryPiAPlus => write!(f, "Raspberry Pi A+"),
            Model::RaspberryPiB => write!(f, "Raspberry Pi B"),
            Model::RaspberryPiBRev1 => write!(f, "Raspberry Pi B Rev 1"),
            Model::RaspberryPiBRev2 => write!(f, "Raspberry Pi B Rev 2"),
            Model::RaspberryPiBPlus => write!(f, "Raspberry Pi B+"),
            Model::RaspberryPi2B => write!(f, "Raspberry Pi 2 B"),
            Model::RaspberryPi3B => write!(f, "Raspberry Pi 3 B"),
            Model::RaspberryPi3BPlus => write!(f, "Raspberry Pi 3 B+"),
            Model::RaspberryPiComputeModule => write!(f, "Raspberry Pi Compute Module"),
            Model::RaspberryPiComputeModule3 => write!(f, "Raspberry Pi Compute Module 3"),
            Model::RaspberryPiZero => write!(f, "Raspberry Pi Zero"),
            Model::RaspberryPiZeroW => write!(f, "Raspberry Pi Zero W"),
        }
    }
}

/// Identifiable Raspberry Pi SoCs.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SoC {
    Bcm2835,
    Bcm2836,
    Bcm2837A1,
    Bcm2837B0,
}

impl fmt::Display for SoC {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SoC::Bcm2835 => write!(f, "BCM2835"),
            SoC::Bcm2836 => write!(f, "BCM2836"),
            SoC::Bcm2837A1 => write!(f, "BCM2837A1"),
            SoC::Bcm2837B0 => write!(f, "BCM2837B0"),
        }
    }
}

// Identify Pi model based on /proc/cpuinfo
fn parse_proc_cpuinfo() -> Result<Model> {
    let proc_cpuinfo = BufReader::new(match File::open("/proc/cpuinfo") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(file) => file,
    });

    let mut hardware: String = String::new();
    let mut revision: String = String::new();
    for line_result in proc_cpuinfo.lines() {
        if let Ok(line) = line_result {
            if line.starts_with("Hardware\t: ") {
                hardware = String::from(&line[11..]);
            } else if line.starts_with("Revision\t: ") {
                revision = String::from(&line[11..]).to_lowercase();
            }
        }
    }

    // Return an error if we don't recognize the SoC. This check is
    // done to prevent accidentally identifying a non-Pi SBC as a Pi
    // solely based on the revision field.
    match &hardware[..] {
        "BCM2708" | "BCM2835" | "BCM2709" | "BCM2836" | "BCM2710" | "BCM2837" | "BCM2837A1"
        | "BCM2837B0" => {}
        _ => return Err(Error::UnknownModel),
    }

    let model = if (revision.len() == 4) || (revision.len() == 8) {
        // Older revisions are 4 characters long, or 8 if they've been over-volted
        match &revision[revision.len() - 4..] {
            "0007" | "0008" | "0009" | "0015" => Model::RaspberryPiA,
            "Beta" | "0002" | "0003" => Model::RaspberryPiBRev1,
            "0004" | "0005" | "0006" | "000d" | "000e" | "000f" => Model::RaspberryPiBRev2,
            "0012" => Model::RaspberryPiAPlus,
            "0010" | "0013" => Model::RaspberryPiBPlus,
            "0011" | "0014" => Model::RaspberryPiComputeModule,
            _ => return Err(Error::UnknownModel),
        }
    } else if revision.len() >= 6 {
        // Newer revisions consist of at least 6 characters
        match &revision[revision.len() - 3..revision.len() - 1] {
            "00" => Model::RaspberryPiA,
            "01" => Model::RaspberryPiBRev2,
            "02" => Model::RaspberryPiAPlus,
            "03" => Model::RaspberryPiBPlus,
            "04" => Model::RaspberryPi2B,
            "06" => Model::RaspberryPiComputeModule,
            "08" => Model::RaspberryPi3B,
            "09" => Model::RaspberryPiZero,
            "0a" => Model::RaspberryPiComputeModule3,
            "0c" => Model::RaspberryPiZeroW,
            "0d" => Model::RaspberryPi3BPlus,
            _ => return Err(Error::UnknownModel),
        }
    } else {
        return Err(Error::UnknownModel);
    };

    Ok(model)
}

// Identify Pi model based on /sys/firmware/devicetree/base/compatible
fn parse_firmware_compatible() -> Result<Model> {
    let compatible = match fs::read_to_string("/sys/firmware/devicetree/base/compatible") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(s) => s,
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    for compid in compatible.split("\0") {
        match compid {
            "raspberrypi,model-b-i2c0" => return Ok(Model::RaspberryPiBRev1),
            "raspberrypi,model-b" => return Ok(Model::RaspberryPiB),
            "raspberrypi,model-a" => return Ok(Model::RaspberryPiA),
            "raspberrypi,model-b-rev2" => return Ok(Model::RaspberryPiBRev2),
            "raspberrypi,model-a-plus" => return Ok(Model::RaspberryPiAPlus),
            "raspberrypi,model-b-plus" => return Ok(Model::RaspberryPiBPlus),
            "raspberrypi,2-model-b" => return Ok(Model::RaspberryPi2B),
            "raspberrypi,compute-module" => return Ok(Model::RaspberryPiComputeModule),
            "raspberrypi,3-model-b" => return Ok(Model::RaspberryPi3B),
            "raspberrypi,model-zero" => return Ok(Model::RaspberryPiZero),
            "raspberrypi,3-compute-module" => return Ok(Model::RaspberryPiComputeModule3),
            "raspberrypi,model-zero-w" => return Ok(Model::RaspberryPiZeroW),
            "raspberrypi,3-model-b-plus" => return Ok(Model::RaspberryPi3BPlus),
            _ => (),
        }
    }

    Err(Error::UnknownModel)
}

// Identify Pi model based on /sys/firmware/devicetree/base/model
fn parse_firmware_model() -> Result<Model> {
    let model = match fs::read_to_string("/sys/firmware/devicetree/base/model") {
        Err(_) => return Err(Error::UnknownModel),
        Ok(buffer) => if let Some(rev_idx) = buffer.find(" Rev ") {
            // We don't want to strip rev2 here
            buffer[0..rev_idx].to_owned()
        } else {
            buffer
        },
    };

    // Based on /arch/arm/boot/dts/ and /Documentation/devicetree/bindings/arm/bcm/
    match &model[..] {
        "Raspberry Pi Model B (no P5)" => return Ok(Model::RaspberryPiBRev1),
        "Raspberry Pi Model B" => return Ok(Model::RaspberryPiB),
        "Raspberry Pi Model A" => return Ok(Model::RaspberryPiA),
        "Raspberry Pi Model B rev2" => return Ok(Model::RaspberryPiBRev2),
        "Raspberry Pi Model A+" => return Ok(Model::RaspberryPiAPlus),
        "Raspberry Pi Model B+" => return Ok(Model::RaspberryPiBPlus),
        "Raspberry Pi 2 Model B" => return Ok(Model::RaspberryPi2B),
        "Raspberry Pi Compute Module" => return Ok(Model::RaspberryPiComputeModule),
        "Raspberry Pi 3 Model B" => return Ok(Model::RaspberryPi3B),
        "Raspberry Pi Zero" => return Ok(Model::RaspberryPiZero),
        "Raspberry Pi Compute Module 3" => return Ok(Model::RaspberryPiComputeModule3),
        "Raspberry Pi Zero W" => return Ok(Model::RaspberryPiZeroW),
        "Raspberry Pi 3 Model B+" => return Ok(Model::RaspberryPi3BPlus),
        _ => return Err(Error::UnknownModel),
    }
}

/// Retrieves Raspberry Pi device information.
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct DeviceInfo {
    model: Model,
    soc: SoC,
    peripheral_base: u32,
    gpio_offset: u32,
}

impl DeviceInfo {
    /// Constructs a new `DeviceInfo`.
    ///
    /// `new` parses the contents of `/proc/cpuinfo` to identify the Raspberry
    /// Pi's model and SoC.
    pub fn new() -> Result<DeviceInfo> {
        let model =
            parse_proc_cpuinfo().or(parse_firmware_compatible().or(parse_firmware_model()))?;

        // Set SoC and memory offsets based on model
        match model {
            Model::RaspberryPiA
            | Model::RaspberryPiAPlus
            | Model::RaspberryPiB
            | Model::RaspberryPiBRev1
            | Model::RaspberryPiBRev2
            | Model::RaspberryPiBPlus
            | Model::RaspberryPiComputeModule
            | Model::RaspberryPiZero
            | Model::RaspberryPiZeroW => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2835,
                peripheral_base: PERIPHERAL_BASE_RPI,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi2B => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2836,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3B | Model::RaspberryPiComputeModule3 => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837A1,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
            Model::RaspberryPi3BPlus => Ok(DeviceInfo {
                model,
                soc: SoC::Bcm2837B0,
                peripheral_base: PERIPHERAL_BASE_RPI2,
                gpio_offset: GPIO_OFFSET,
            }),
        }
    }

    /// Returns the Raspberry Pi's model.
    pub fn model(&self) -> Model {
        self.model
    }

    /// Returns the Raspberry Pi's SoC.
    pub fn soc(&self) -> SoC {
        self.soc
    }

    /// Returns the base memory address for the BCM283x peripherals.
    pub fn peripheral_base(&self) -> u32 {
        self.peripheral_base
    }

    /// Returns the offset from the base memory address for the GPIO section.
    pub fn gpio_offset(&self) -> u32 {
        self.gpio_offset
    }
}
<|endoftext|>