<commit_before>extern crate rand;
extern crate rustc_serialize;

/// Width of game board
#[cfg(not(test))] pub const WIDTH: usize = 8;
#[cfg(test)]      pub const WIDTH: usize = 3;
/// Height of game board
#[cfg(not(test))] pub const HEIGHT: usize = 8;
#[cfg(test)]      pub const HEIGHT: usize = 3;
/// Number of rows
#[cfg(not(test))] pub const ROWS: usize = 2;
#[cfg(test)]      pub const ROWS: usize = 1;

const RED: Player = Player::Red;
const BLUE: Player = Player::Blue;

const ROCK: RPS = RPS::Rock;
const PAPER: RPS = RPS::Paper;
const SCISSORS: RPS = RPS::Scissors;

const WIN: Outcome = Outcome::Win;
const LOSE: Outcome = Outcome::Lose;
const DRAW: Outcome = Outcome::Draw;

pub mod moves;
pub mod win_conditions;
pub mod unit;
pub mod field;

use moves::{is_valid, Move};
use win_conditions::{WinCondition};
use field::{Field, PovField};
use unit::{Unit, GeneralUnit};

use std::marker::PhantomData;

#[derive(Clone, Debug)]
pub struct Game<T: WinCondition<GeneralUnit>> {
    turns: u32,
    current_turn: Player,
    winner: Option<Player>,
    field: Field<GeneralUnit>,
    rules: Rules<GeneralUnit, T>,
}

impl<T: WinCondition<GeneralUnit>> Game<T> {
    pub fn new(rules: Rules<GeneralUnit, T>) -> Game<T> {
        let mut rows = [[None; WIDTH]; HEIGHT];
        for i in 0..ROWS {
            rows[i] = [Some(RED.random_unit()); WIDTH];
            rows[HEIGHT - i - 1] = [Some(BLUE.random_unit()); WIDTH];
        }
        let field = Field { rows: rows };
        Game {
            turns: 1,
            current_turn: RED,
            winner: None,
            field: field,
            rules: rules,
        }
    }

    pub fn turns(&self) -> u32 { self.turns }
    pub fn current_turn(&self) -> Player { self.current_turn }
    pub fn winner(&self) -> Option<Player> { self.winner }
    pub fn field(&self) -> &Field<GeneralUnit> { &self.field }

    pub fn force_win(&mut self, player: Player) {
        if !self.winner.is_some() { self.winner = Some(player); }
    }

    pub fn perspective(&self, player: Player) -> PovField {
        PovField::from((&self.field, player))
    }

    pub fn make_move(&mut self, movement: Move) -> Result<Option<Outcome>, MoveError> {
        if self.winner.is_some() { return Err(MoveError::GameAlreadyFinished); }

        if !is_valid(movement) {
            return Err(MoveError::DeclinedByMoveCondition);
        }

        let (from_x, from_y) = movement.from;

        if from_x >= WIDTH || from_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

        let attack_outcome;
        let (to_x, to_y);

        if let Some(ref unit) = self.field.rows[from_y][from_x].as_ref() {
            if unit.owner != self.current_turn { return Err(MoveError::WrongOwner); }
            let dist = movement.apply(unit.owner);
            to_x = dist.0;
            to_y = dist.1;
            if to_x >= WIDTH || to_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

            if let Some(ref defender) = self.field.rows[to_y][to_x].as_ref() {
                if defender.owner == self.current_turn { return Err(MoveError::SameOwner); }

                match unit.attack(defender) {
                    Some(res) => {
                        attack_outcome = Some(res);
                    },
                    None => { return Err(MoveError::UnexpextedError); }
                }
            } else {
                attack_outcome = None;
            }

        } else {
            return Err(MoveError::NoUnitInPosition);
        }

        if let Some(outcome) = attack_outcome {
            match outcome {
                WIN => {
                    self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                LOSE => {
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                DRAW => {
                    self.field.rows[from_y][from_x].as_mut().unwrap().visible = true;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                }
            }
        } else {
            self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
            self.field.rows[from_y][from_x] = None;
        }

        self.winner = self.rules.win_condition.winner(&self.field);
        self.turns += 1;
        self.current_turn = self.current_turn.next();

        Ok(attack_outcome)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveError {
    GameAlreadyFinished,
    DeclinedByMoveCondition,
    PositionOutOfBounds,
    WrongOwner,
    NoUnitInPosition,
    SameOwner,
    UnexpextedError,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum Player {
    Red,
    Blue,
}

impl Player {
    fn next(&self) -> Player {
        match *self {
            RED => BLUE,
            BLUE => RED,
        }
    }

    fn unit(&self, rps: RPS) -> GeneralUnit {
        GeneralUnit::new(rps, *self)
    }

    fn random_unit(&self) -> GeneralUnit {
        self.unit(RPS::random())
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum RPS {
    Rock,
    Paper,
    Scissors,
}

impl RPS {
    fn attack(&self, opponent: RPS) -> Outcome {
        match (*self, opponent) {
            (PAPER, ROCK) | (ROCK, SCISSORS) | (SCISSORS, PAPER) => WIN,
            (ROCK, PAPER) | (SCISSORS, ROCK) | (PAPER, SCISSORS) => LOSE,
            _ => DRAW,
        }
    }

    fn random() -> RPS {
        match rand::random::<usize>() % 3 {
            0 => ROCK,
            1 => PAPER,
            2 => SCISSORS,
            _ => { panic!("rand::random::<usize>() % 3 returned not 0, nor 1, nor 2"); }
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Outcome {
    Win,
    Lose,
    Draw,
}

#[derive(Clone, Debug)]
pub struct Rules<K, T: WinCondition<K>> where K: Unit {
    pub win_condition: T,
    phantom_data: PhantomData<K>,
}

impl<K: Unit, T: WinCondition<K>> Rules<K, T> {
    pub fn new(win_condition: T) -> Rules<K, T> {
        Rules {
            win_condition: win_condition,
            phantom_data: PhantomData,
        }
    }
}

#[test]
fn basic_test() {
    use moves::{Direction};
    use win_conditions::EliminateCondition;

    let rules = Rules::new(EliminateCondition);
    let mut game = Game::new(rules);
    let move1 = Move::new(0, 0, Direction::Forward);

    assert_eq!(game.make_move(move1), Ok(None));

    assert_eq!(game.make_move(move1), Err(MoveError::NoUnitInPosition));

    let move2 = Move::new(0, 2, Direction::Forward);
    assert!(game.make_move(move2).unwrap().is_some());
}
<commit_msg>When two visible units draw during attack, they get random RPS<commit_after>extern crate rand;
extern crate rustc_serialize;

/// Width of game board
#[cfg(not(test))] pub const WIDTH: usize = 8;
#[cfg(test)]      pub const WIDTH: usize = 3;
/// Height of game board
#[cfg(not(test))] pub const HEIGHT: usize = 8;
#[cfg(test)]      pub const HEIGHT: usize = 3;
/// Number of rows
#[cfg(not(test))] pub const ROWS: usize = 2;
#[cfg(test)]      pub const ROWS: usize = 1;

const RED: Player = Player::Red;
const BLUE: Player = Player::Blue;

const ROCK: RPS = RPS::Rock;
const PAPER: RPS = RPS::Paper;
const SCISSORS: RPS = RPS::Scissors;

const WIN: Outcome = Outcome::Win;
const LOSE: Outcome = Outcome::Lose;
const DRAW: Outcome = Outcome::Draw;

pub mod moves;
pub mod win_conditions;
pub mod unit;
pub mod field;

use moves::{is_valid, Move};
use win_conditions::{WinCondition};
use field::{Field, PovField};
use unit::{Unit, GeneralUnit};

use std::marker::PhantomData;

#[derive(Clone, Debug)]
pub struct Game<T: WinCondition<GeneralUnit>> {
    turns: u32,
    current_turn: Player,
    winner: Option<Player>,
    field: Field<GeneralUnit>,
    rules: Rules<GeneralUnit, T>,
}

impl<T: WinCondition<GeneralUnit>> Game<T> {
    pub fn new(rules: Rules<GeneralUnit, T>) -> Game<T> {
        let mut rows = [[None; WIDTH]; HEIGHT];
        for i in 0..ROWS {
            rows[i] = [Some(RED.random_unit()); WIDTH];
            rows[HEIGHT - i - 1] = [Some(BLUE.random_unit()); WIDTH];
        }
        let field = Field { rows: rows };
        Game {
            turns: 1,
            current_turn: RED,
            winner: None,
            field: field,
            rules: rules,
        }
    }

    pub fn turns(&self) -> u32 { self.turns }
    pub fn current_turn(&self) -> Player { self.current_turn }
    pub fn winner(&self) -> Option<Player> { self.winner }
    pub fn field(&self) -> &Field<GeneralUnit> { &self.field }

    pub fn force_win(&mut self, player: Player) {
        if !self.winner.is_some() { self.winner = Some(player); }
    }

    pub fn perspective(&self, player: Player) -> PovField {
        PovField::from((&self.field, player))
    }

    pub fn make_move(&mut self, movement: Move) -> Result<Option<Outcome>, MoveError> {
        if self.winner.is_some() { return Err(MoveError::GameAlreadyFinished); }

        if !is_valid(movement) {
            return Err(MoveError::DeclinedByMoveCondition);
        }

        let (from_x, from_y) = movement.from;

        if from_x >= WIDTH || from_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

        let attack_outcome;
        let (to_x, to_y);

        if let Some(ref unit) = self.field.rows[from_y][from_x].as_ref() {
            if unit.owner != self.current_turn { return Err(MoveError::WrongOwner); }
            let dist = movement.apply(unit.owner);
            to_x = dist.0;
            to_y = dist.1;
            if to_x >= WIDTH || to_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

            if let Some(ref defender) = self.field.rows[to_y][to_x].as_ref() {
                if defender.owner == self.current_turn { return Err(MoveError::SameOwner); }

                match unit.attack(defender) {
                    Some(res) => {
                        attack_outcome = Some(res);
                    },
                    None => { return Err(MoveError::UnexpextedError); }
                }
            } else {
                attack_outcome = None;
            }

        } else {
            return Err(MoveError::NoUnitInPosition);
        }

        if let Some(outcome) = attack_outcome {
            match outcome {
                WIN => {
                    self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                LOSE => {
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                DRAW => {
                    let v1 = self.field.rows[from_y][from_x].as_mut().unwrap().visible;
                    let v2 = self.field.rows[to_y][to_x].as_mut().unwrap().visible;

                    if v1 && v2{
                        self.field.rows[from_y][from_x].as_mut().unwrap().rps = RPS::random();
                        self.field.rows[to_y][to_x].as_mut().unwrap().rps = RPS::random();
                    } else {
                        self.field.rows[from_y][from_x].as_mut().unwrap().visible = true;
                        self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                    }
                }
            }
        } else {
            self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
            self.field.rows[from_y][from_x] = None;
        }

        self.winner = self.rules.win_condition.winner(&self.field);
        self.turns += 1;
        self.current_turn = self.current_turn.next();

        Ok(attack_outcome)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveError {
    GameAlreadyFinished,
    DeclinedByMoveCondition,
    PositionOutOfBounds,
    WrongOwner,
    NoUnitInPosition,
    SameOwner,
    UnexpextedError,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum Player {
    Red,
    Blue,
}

impl Player {
    fn next(&self) -> Player {
        match *self {
            RED => BLUE,
            BLUE => RED,
        }
    }

    fn unit(&self, rps: RPS) -> GeneralUnit {
        GeneralUnit::new(rps, *self)
    }

    fn random_unit(&self) -> GeneralUnit {
        self.unit(RPS::random())
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum RPS {
    Rock,
    Paper,
    Scissors,
}

impl RPS {
    fn attack(&self, opponent: RPS) -> Outcome {
        match (*self, opponent) {
            (PAPER, ROCK) | (ROCK, SCISSORS) | (SCISSORS, PAPER) => WIN,
            (ROCK, PAPER) | (SCISSORS, ROCK) | (PAPER, SCISSORS) => LOSE,
            _ => DRAW,
        }
    }

    fn random() -> RPS {
        match rand::random::<usize>() % 3 {
            0 => ROCK,
            1 => PAPER,
            2 => SCISSORS,
            _ => { panic!("rand::random::<usize>() % 3 returned not 0, nor 1, nor 2"); }
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Outcome {
    Win,
    Lose,
    Draw,
}

#[derive(Clone, Debug)]
pub struct Rules<K, T: WinCondition<K>> where K: Unit {
    pub win_condition: T,
    phantom_data: PhantomData<K>,
}

impl<K: Unit, T: WinCondition<K>> Rules<K, T> {
    pub fn new(win_condition: T) -> Rules<K, T> {
        Rules {
            win_condition: win_condition,
            phantom_data: PhantomData,
        }
    }
}

#[test]
fn basic_test() {
    use moves::{Direction};
    use win_conditions::EliminateCondition;

    let rules = Rules::new(EliminateCondition);
    let mut game = Game::new(rules);
    let move1 = Move::new(0, 0, Direction::Forward);

    assert_eq!(game.make_move(move1), Ok(None));

    assert_eq!(game.make_move(move1), Err(MoveError::NoUnitInPosition));

    let move2 = Move::new(0, 2, Direction::Forward);
    assert!(game.make_move(move2).unwrap().is_some());
}
<|endoftext|>


<commit_before>extern crate uuid;

use uuid::Uuid;
use std::intrinsics::TypeId;
use std::collections::hashmap::HashMap;
use std::collections::hashmap::{Vacant, Occupied};
use std::any::{Any, AnyRefExt, AnyMutRefExt};

pub trait Component {
    fn get_id(&self) -> Uuid;
}

type ComponentVec = Vec<Box<Any>>;
type EntityMap = HashMap<Uuid, ComponentVec>;

pub struct EntityManager {
    named_entities: HashMap<&'static str, Uuid>,
    components: HashMap<TypeId, EntityMap>,
}

impl EntityManager {
    pub fn new() -> EntityManager {
        EntityManager {
            named_entities: HashMap::new(),
            components: HashMap::new(),
        }
    }

    pub fn create_entity(&self) -> Uuid {
        Uuid::new_v4()
    }

    pub fn create_named_entity(&mut self, name: &'static str) -> Uuid {
        let entity = self.create_entity();
        self.named_entities.insert(name, entity);
        entity
    }

    pub fn get_named_entity(&self, name: &'static str) -> Result<Uuid, String> {
        match self.named_entities.find(&name) {
            None => Err(format!("could not find entity for name: {}", name)),
            Some(entity) => Ok(*entity),
        }
    }

    pub fn insert<T:Component+'static>(&mut self, entity:Uuid, component:T) {
        let entity_map = match self.components.entry(TypeId::of::<T>()) {
            Vacant(entry) => entry.set(HashMap::new()),
            Occupied(entry) => entry.into_mut(),
        };

        let component_vec = match entity_map.entry(entity) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut(),
        };

        component_vec.push(box component as Box<Any>);
    }

    pub fn find_entities<T:Component+'static>(&self) -> Vec<Uuid> {
        let mut result:Vec<Uuid> = Vec::new();
        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                for (entity, _) in entity_map.iter() {
                    result.push(*entity);
                }
            }
        }
        result
    }

    pub fn find<T:Component+Clone+'static>(&self) -> Vec<T> {
        let mut result:Vec<T> = Vec::new();

        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                result.reserve_additional(entity_map.len());
                for (_, component_vec) in entity_map.iter() {
                    if component_vec.len() > 1 {
                        result.reserve_additional(component_vec.len()-1);
                    }
                    for component in component_vec.iter() {
                        result.push(component.downcast_ref::<T>().unwrap().clone());
                    }
                }
            }
        }

        result
    }

    pub fn find_mut<T:Component+'static>(&mut self) -> Vec<&mut T> {
        let mut result:Vec<&mut T> = Vec::new();

        match self.components.find_mut(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                result.reserve_additional(entity_map.len());
                for (_, component_vec) in entity_map.iter_mut() {
                    if component_vec.len() > 1 {
                        result.reserve_additional(component_vec.len()-1);
                    }
                    for component in component_vec.iter_mut() {
                        result.push(component.downcast_mut::<T>().unwrap());
                    }
                }
            }
        }

        result
    }

    pub fn find_for<T:Component+Clone+'static>(&self, entity:Uuid) -> Vec<T> {
        let mut result:Vec<T> = Vec::new();

        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                match entity_map.find(&entity) {
                    None => {},
                    Some(component_vec) => {
                        result.reserve_additional(component_vec.len());
                        for component in component_vec.iter() {
                            result.push(component.downcast_ref::<T>().unwrap().clone());
                        }
                    }
                }
            }

        }

        result
    }

    pub fn find_for_mut<T:Component+'static>(&mut self, entity:Uuid) -> Vec<&mut T> {
        let mut result:Vec<&mut T> = Vec::new();

        match self.components.find_mut(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                match entity_map.find_mut(&entity) {
                    None => {},
                    Some(component_vec) => {
                        for component in component_vec.iter_mut() {
                            result.push(component.downcast_mut::<T>().unwrap())
                        }
                    }
                }
            }

        }

        result
    }
}

<commit_msg>Move to the new syntax for trait constraining generic types<commit_after>extern crate uuid;

use uuid::Uuid;
use std::intrinsics::TypeId;
use std::collections::hashmap::HashMap;
use std::collections::hashmap::{Vacant, Occupied};
use std::any::{Any, AnyRefExt, AnyMutRefExt};

pub trait Component {
    fn get_id(&self) -> Uuid;
}

type ComponentVec = Vec<Box<Any>>;
type EntityMap = HashMap<Uuid, ComponentVec>;

pub struct EntityManager {
    named_entities: HashMap<&'static str, Uuid>,
    components: HashMap<TypeId, EntityMap>,
}

impl EntityManager {
    pub fn new() -> EntityManager
    {
        EntityManager {
            named_entities: HashMap::new(),
            components: HashMap::new(),
        }
    }

    pub fn create_entity(&self) -> Uuid
    {
        Uuid::new_v4()
    }

    pub fn create_named_entity(&mut self, name: &'static str) -> Uuid
    {
        let entity = self.create_entity();
        self.named_entities.insert(name, entity);
        entity
    }

    pub fn get_named_entity(&self, name: &'static str) -> Result<Uuid, String>
    {
        match self.named_entities.find(&name) {
            None => Err(format!("could not find entity for name: {}", name)),
            Some(entity) => Ok(*entity),
        }
    }

    pub fn insert<T>(&mut self, entity:Uuid, component:T)
        where T: Component+'static
    {
        let entity_map = match self.components.entry(TypeId::of::<T>()) {
            Vacant(entry) => entry.set(HashMap::new()),
            Occupied(entry) => entry.into_mut(),
        };

        let component_vec = match entity_map.entry(entity) {
            Vacant(entry) => entry.set(Vec::new()),
            Occupied(entry) => entry.into_mut(),
        };

        component_vec.push(box component as Box<Any>);
    }

    pub fn find_entities<T>(&self) -> Vec<Uuid>
        where T: Component+'static
    {
        let mut result:Vec<Uuid> = Vec::new();
        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                for (entity, _) in entity_map.iter() {
                    result.push(*entity);
                }
            }
        }
        result
    }

    pub fn find<T>(&self) -> Vec<T>
        where T: Component+Clone+'static
    {
        let mut result:Vec<T> = Vec::new();

        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                result.reserve_additional(entity_map.len());
                for (_, component_vec) in entity_map.iter() {
                    if component_vec.len() > 1 {
                        result.reserve_additional(component_vec.len()-1);
                    }
                    for component in component_vec.iter() {
                        result.push(component.downcast_ref::<T>().unwrap().clone());
                    }
                }
            }
        }

        result
    }

    pub fn find_mut<T>(&mut self) -> Vec<&mut T>
        where T: Component+'static
    {
        let mut result:Vec<&mut T> = Vec::new();

        match self.components.find_mut(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                result.reserve_additional(entity_map.len());
                for (_, component_vec) in entity_map.iter_mut() {
                    if component_vec.len() > 1 {
                        result.reserve_additional(component_vec.len()-1);
                    }
                    for component in component_vec.iter_mut() {
                        result.push(component.downcast_mut::<T>().unwrap());
                    }
                }
            }
        }

        result
    }

    pub fn find_for<T>(&self, entity:Uuid) -> Vec<T>
        where T: Component+Clone+'static
    {
        let mut result:Vec<T> = Vec::new();

        match self.components.find(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                match entity_map.find(&entity) {
                    None => {},
                    Some(component_vec) => {
                        result.reserve_additional(component_vec.len());
                        for component in component_vec.iter() {
                            result.push(component.downcast_ref::<T>().unwrap().clone());
                        }
                    }
                }
            }

        }

        result
    }

    pub fn find_for_mut<T>(&mut self, entity:Uuid) -> Vec<&mut T>
        where T: Component+'static
    {
        let mut result:Vec<&mut T> = Vec::new();

        match self.components.find_mut(&TypeId::of::<T>()) {
            None => {},
            Some(entity_map) => {
                match entity_map.find_mut(&entity) {
                    None => {},
                    Some(component_vec) => {
                        for component in component_vec.iter_mut() {
                            result.push(component.downcast_mut::<T>().unwrap())
                        }
                    }
                }
            }

        }

        result
    }
}

<|endoftext|>


<commit_before>extern crate libc;

pub mod groonga;

use libc::funcs::c95::string;
use std::ffi::CString;
use std::fs;
use std::mem;
use std::ptr;
use std::result::Result;
use std::result::Result::{Ok, Err};

#[derive(Debug)]
pub struct Error {
    code: i32
}

impl Error {
    pub fn new(code: i32) -> Error {
        Error {code: code}
    }
}

pub struct Groonga {
    closed: bool
}

impl Groonga {
    pub fn new() -> Result<Groonga, Error> {
        unsafe {
            let rc =  groonga::grn_init();
            if rc != groonga::GRN_SUCCESS {
                return Err(Error::new(rc))
            }
            Ok(Groonga{ closed: false })
        }
    }

    pub fn close(&mut self) -> Result<(), Error> {
        if self.closed {
            return Ok(())
        }
        unsafe {
            let rc = groonga::grn_fin();
            if rc != groonga::GRN_SUCCESS {
                return Err(Error::new(rc))
            }
            self.closed = true;
            Ok(())
        }
    }
}

impl Drop for Groonga {
    fn drop(&mut self) {
        self.close().unwrap()
    }
}

pub struct Context {
    ctx: *mut groonga::grn_ctx,
}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            let rc = groonga::grn_ctx_fin(self.ctx);
            if rc != groonga::GRN_SUCCESS {
                panic!("grn_ctx_fin(ctx) failed with rc={}", rc);
            }
        }
    }
}

impl Context {
    pub fn new() -> Result<Context, Error> {
        unsafe {
            let ctx = groonga::grn_ctx_open(0);
            if ctx.is_null() {
                return Err(Error::new(groonga::GRN_NO_MEMORY_AVAILABLE))
            }
            Ok(Context { ctx: ctx })
        }
    }

    pub fn db_create(&mut self, path: &str) -> Result<(), Error> {
        let c_path = CString::new(path).unwrap();
        unsafe {
            let db = groonga::grn_db_create(
                self.ctx, c_path.as_ptr(), mem::zeroed());
            if db.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(())
        }
    }

    pub fn db_open(&mut self, path: &str) -> Result<(), Error> {
        let c_path = CString::new(path).unwrap();
        unsafe {
            let db = groonga::grn_db_open(self.ctx, c_path.as_ptr());
            if db.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(())
        }
    }

    pub fn db_open_or_create(&mut self, path: &str) -> Result<(), Error> {
        if file_exists(path) {
            self.db_open(path)
        } else {
            self.db_create(path)
        }
    }

    pub fn ctx_at(&mut self, id: u32) -> Result<Object, Error> {
        unsafe {
            let obj = groonga::grn_ctx_at(self.ctx, id);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn ctx_get(&mut self, name: &str) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        unsafe {
            let obj = groonga::grn_ctx_get(
                self.ctx, c_name, string::strlen(c_name) as i32);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn table_create(&mut self, name: &str, path: &str, flags: u32,
        key_type: &Object, value_type: Option<&Object>) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        let c_path = if path != "" {
            CString::new(path).unwrap().as_ptr()
        } else {
            ptr::null()
        };
        unsafe {
            let c_value_type =
                match value_type {
                    Some(value_type_obj) => value_type_obj.obj,
                    None => mem::zeroed(),
                };
            let table = groonga::grn_table_create(
                self.ctx, c_name, string::strlen(c_name) as u32, c_path,
                flags as u16, key_type.obj, c_value_type);
            if table.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: table })
        }
    }

    pub fn table_open_or_create(&mut self, name: &str, path: &str, flags: u32,
        key_type: &Object, value_type: Option<&Object>) -> Result<Object, Error> {
        if let Ok(table) = self.ctx_get(name) {
            Ok(table)
        } else {
            self.table_create(name, path, flags,
                key_type, value_type)
        }
    }

    pub fn obj_column(&mut self, table: &Object, name: &str)
        -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        unsafe {
            let obj = groonga::grn_obj_column(
                self.ctx, table.obj, c_name, string::strlen(c_name) as u32);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn column_create(&mut self, table: &Object, name: &str, path: &str,
        flags: u32, _type: &Object) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        let c_path = if path != "" {
            CString::new(path).unwrap().as_ptr()
        } else {
            ptr::null()
        };
        unsafe {
            let column = groonga::grn_column_create(
                self.ctx, table.obj, c_name, string::strlen(c_name) as u32,
                c_path, flags as u16, _type.obj);
            if column.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: column })
        }
    }

    pub fn column_open_or_create(&mut self, table: &Object, name: &str,
        path: &str, flags: u32, _type: &Object) -> Result<Object, Error> {
        if let Ok(column) = self.obj_column(&table, name) {
            Ok(column)
        } else {
            self.column_create(&table, name, path, flags, _type)
        }
    }
}

pub struct Object {
    context: *mut Context,
    obj: *mut groonga::grn_obj,
}

impl Object {
    pub fn close(&mut self) {
        unsafe {
            if self.obj.is_null() {
                return
            }
            groonga::grn_obj_unlink((*self.context).ctx, self.obj);
            self.obj = mem::zeroed()
        }
    }
}

fn file_exists(path: &str) -> bool {
    match fs::File::open(path) {
        Ok(_) => true,
        Err(_) => false,
    }
}
<commit_msg>Add close method to Context<commit_after>extern crate libc;

pub mod groonga;

use libc::funcs::c95::string;
use std::ffi::CString;
use std::fs;
use std::mem;
use std::ptr;
use std::result::Result;
use std::result::Result::{Ok, Err};

#[derive(Debug)]
pub struct Error {
    code: i32
}

impl Error {
    pub fn new(code: i32) -> Error {
        Error {code: code}
    }
}

pub struct Groonga {
    closed: bool
}

impl Groonga {
    pub fn new() -> Result<Groonga, Error> {
        unsafe {
            let rc =  groonga::grn_init();
            if rc != groonga::GRN_SUCCESS {
                return Err(Error::new(rc))
            }
            Ok(Groonga{ closed: false })
        }
    }

    pub fn close(&mut self) -> Result<(), Error> {
        if self.closed {
            return Ok(())
        }
        unsafe {
            let rc = groonga::grn_fin();
            if rc != groonga::GRN_SUCCESS {
                return Err(Error::new(rc))
            }
            self.closed = true;
            Ok(())
        }
    }
}

impl Drop for Groonga {
    fn drop(&mut self) {
        self.close().unwrap()
    }
}

pub struct Context {
    ctx: *mut groonga::grn_ctx,
}

impl Drop for Context {
    fn drop(&mut self) {
        self.close().unwrap()
    }
}

impl Context {
    pub fn new() -> Result<Context, Error> {
        unsafe {
            let ctx = groonga::grn_ctx_open(0);
            if ctx.is_null() {
                return Err(Error::new(groonga::GRN_NO_MEMORY_AVAILABLE))
            }
            Ok(Context { ctx: ctx })
        }
    }

    pub fn close(&mut self) -> Result<(), Error> {
        unsafe {
            if self.ctx.is_null() {
                return Ok(())
            }
            let rc = groonga::grn_ctx_fin(self.ctx);
            if rc != groonga::GRN_SUCCESS {
                return Err(Error::new(rc))
            }
            self.ctx = mem::zeroed();
            Ok(())
        }
    }

    pub fn db_create(&mut self, path: &str) -> Result<(), Error> {
        let c_path = CString::new(path).unwrap();
        unsafe {
            let db = groonga::grn_db_create(
                self.ctx, c_path.as_ptr(), mem::zeroed());
            if db.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(())
        }
    }

    pub fn db_open(&mut self, path: &str) -> Result<(), Error> {
        let c_path = CString::new(path).unwrap();
        unsafe {
            let db = groonga::grn_db_open(self.ctx, c_path.as_ptr());
            if db.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(())
        }
    }

    pub fn db_open_or_create(&mut self, path: &str) -> Result<(), Error> {
        if file_exists(path) {
            self.db_open(path)
        } else {
            self.db_create(path)
        }
    }

    pub fn ctx_at(&mut self, id: u32) -> Result<Object, Error> {
        unsafe {
            let obj = groonga::grn_ctx_at(self.ctx, id);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn ctx_get(&mut self, name: &str) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        unsafe {
            let obj = groonga::grn_ctx_get(
                self.ctx, c_name, string::strlen(c_name) as i32);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn table_create(&mut self, name: &str, path: &str, flags: u32,
        key_type: &Object, value_type: Option<&Object>) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        let c_path = if path != "" {
            CString::new(path).unwrap().as_ptr()
        } else {
            ptr::null()
        };
        unsafe {
            let c_value_type =
                match value_type {
                    Some(value_type_obj) => value_type_obj.obj,
                    None => mem::zeroed(),
                };
            let table = groonga::grn_table_create(
                self.ctx, c_name, string::strlen(c_name) as u32, c_path,
                flags as u16, key_type.obj, c_value_type);
            if table.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: table })
        }
    }

    pub fn table_open_or_create(&mut self, name: &str, path: &str, flags: u32,
        key_type: &Object, value_type: Option<&Object>) -> Result<Object, Error> {
        if let Ok(table) = self.ctx_get(name) {
            Ok(table)
        } else {
            self.table_create(name, path, flags,
                key_type, value_type)
        }
    }

    pub fn obj_column(&mut self, table: &Object, name: &str)
        -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        unsafe {
            let obj = groonga::grn_obj_column(
                self.ctx, table.obj, c_name, string::strlen(c_name) as u32);
            if obj.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: obj })
        }
    }

    pub fn column_create(&mut self, table: &Object, name: &str, path: &str,
        flags: u32, _type: &Object) -> Result<Object, Error> {
        let c_name = CString::new(name).unwrap().as_ptr();
        let c_path = if path != "" {
            CString::new(path).unwrap().as_ptr()
        } else {
            ptr::null()
        };
        unsafe {
            let column = groonga::grn_column_create(
                self.ctx, table.obj, c_name, string::strlen(c_name) as u32,
                c_path, flags as u16, _type.obj);
            if column.is_null() {
                return Err(Error::new((*self.ctx).rc))
            }
            Ok(Object { context: self, obj: column })
        }
    }

    pub fn column_open_or_create(&mut self, table: &Object, name: &str,
        path: &str, flags: u32, _type: &Object) -> Result<Object, Error> {
        if let Ok(column) = self.obj_column(&table, name) {
            Ok(column)
        } else {
            self.column_create(&table, name, path, flags, _type)
        }
    }
}

pub struct Object {
    context: *mut Context,
    obj: *mut groonga::grn_obj,
}

impl Object {
    pub fn close(&mut self) {
        unsafe {
            if self.obj.is_null() {
                return
            }
            groonga::grn_obj_unlink((*self.context).ctx, self.obj);
            self.obj = mem::zeroed()
        }
    }
}

fn file_exists(path: &str) -> bool {
    match fs::File::open(path) {
        Ok(_) => true,
        Err(_) => false,
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(test, feature(test))]

//! sysly is a rust interface for [syslog](https://tools.ietf.org/html/rfc5424)

#[cfg(test)]
extern crate test;

extern crate time;
extern crate unix_socket;

use std::convert::AsRef;
use std::io::{ Error, Write };
use std::net::{ Ipv4Addr, UdpSocket, SocketAddr, SocketAddrV4 };
use std::path::Path;
use std::result;
use time::Tm;
use unix_socket::UnixStream;

/// A type alias for `result::Result<(), std::io::Error>`, the result of writing a log message
pub type Result = result::Result<(), Error>;

static NIL: &'static str = "-";

/// Syslog [Facilities](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  KERN     = 0,
  USER     = 1 << 3,
  MAIL     = 2 << 3,
  DAEMON   = 3 << 3,
  AUTH     = 4 << 3,
  SYSLOG   = 5 << 3,
  LINEPTR  = 6 << 3,
  NEWS     = 7 << 3,
  UUCP     = 8 << 3,
  CLOCK    = 9 << 3,
  AUTHPRIV = 10 << 3,
  FTP      = 11 << 3,
  LOCAL0   = 16 << 3,
  LOCAL1   = 17 << 3,
  LOCAL2   = 18 << 3,
  LOCAL3   = 19 << 3,
  LOCAL4   = 20 << 3,
  LOCAL5   = 21 << 3,
  LOCAL6   = 22 << 3,
  LOCAL7   = 23 << 3
}

/// Syslog [Severities](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  EMERGENCY,
  ALERT,
  CRITICAL,
  ERROR,
  WARNING,
  NOTICE,
  INFO,
  DEBUG
}

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), &self.1).map(|_| ())
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_all(line.as_bytes())
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  /// A Syslog host entry as defined by
  /// [rfc5424#section-6.2.4](https://tools.ietf.org/html/rfc5424#section-6.2.4)
  host: Option<String>,
  /// An optional app-name appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.5](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  app: Option<String>,
  /// An optional proc-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.6](https://tools.ietf.org/html/rfc5424#section-6.2.6)
  pid: Option<String>,
  /// An optional msg-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.7](https://tools.ietf.org/html/rfc5424#section-6.2.7)
  msgid: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind("0.0.0.0:0") {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        app: None,
        pid: None,
        msgid: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 514)))
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix<P: AsRef<Path>>(path: P) -> Syslog {
    let stream =
      match UnixStream::connect(path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      app: None,
      pid: None,
      msgid: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided app-name
  pub fn app(self, app: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: Some(app.to_string()),
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn pid(self, pid: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: Some(pid.to_string()),
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn msgid(self, id: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: Some(id.to_string()),
      transport: self.transport
    }
  }

  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(
        self.facility.clone(), severity, time::now(), self.host.clone(), self.app.clone(), self.pid.clone(), self.msgid.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, app: Option<String>, pid: Option<String>, msgid: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}>1 {} {} {} {} {} {}",
        Syslog::priority(facility, severity),
        timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()),
        app.unwrap_or(NIL.to_string()),
        pid.unwrap_or(NIL.to_string()),
        msgid.unwrap_or(NIL.to_string()),
        msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, Severity};
  use time;
  use test::Bencher;

  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, None, "yo"),
      format!("<134>1 {} - - - - yo", ts.rfc3339()));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, None, None, "yo"),
      format!("<134>1 {} {} - - - yo", ts.rfc3339(), host));
  }

  #[test]
  fn test_syslog_line_app() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), None, None, "yo"),
      format!("<134>1 {} - {} - - yo", ts.rfc3339(), app));
  }

  #[test]
  fn test_syslog_line_pid() {
    let ts = time::now();
    let pid = "16";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, Some(pid.to_string()), None, "yo"),
      format!("<134>1 {} - - {} - yo", ts.rfc3339(), pid));
  }

  #[test]
  fn test_syslog_line_msgid() {
    let ts = time::now();
    let msgid = "TCPIN";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, Some(msgid.to_string()), "yo"),
      format!("<134>1 {} - - - {} yo", ts.rfc3339(), msgid));
  }

  #[bench]
  fn bench_assembly_line(b: &mut Bencher) {
    b.iter(|| Syslog::line(
      Facility::LOCAL0, Severity::INFO, time::now(), None, None, None, None, "yo"))
  }
}
<commit_msg>doc crackdown<commit_after>#![deny(missing_docs)]
//#![cfg_attr(test, deny(warnings))]
#![cfg_attr(all(test, feature = "nightly"), feature(test))]

//! sysly is a rust interface for [syslog](https://tools.ietf.org/html/rfc5424)

//  #[cfg(all(test, feature = "nightly"))]
//  extern crate test;
extern crate time;
extern crate unix_socket;

use std::convert::AsRef;
use std::io::{ self, Write };
use std::net::{ Ipv4Addr, UdpSocket, SocketAddr, SocketAddrV4 };
use std::path::Path;
use time::Tm;
use unix_socket::UnixStream;

static NIL: &'static str = "-";

/// Syslog [Facilities](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  /// kernal facility
  KERN     = 0,
  /// user facility
  USER     = 1 << 3,
  /// user facility
  MAIL     = 2 << 3,
  /// daemon facility
  DAEMON   = 3 << 3,
  /// auth facility
  AUTH     = 4 << 3,
  /// syslog facility
  SYSLOG   = 5 << 3,
  /// lineptr facility
  LINEPTR  = 6 << 3,
  /// news facility
  NEWS     = 7 << 3,
  /// uucp facility
  UUCP     = 8 << 3,
  /// clock facility
  CLOCK    = 9 << 3,
  /// auth facility
  AUTHPRIV = 10 << 3,
  /// ftp facility
  FTP      = 11 << 3,
  /// Local0 facility
  LOCAL0   = 16 << 3,
  /// Local1 facility
  LOCAL1   = 17 << 3,
  /// Local2 facility
  LOCAL2   = 18 << 3,
  /// Local3 facility
  LOCAL3   = 19 << 3,
  /// Local4 facility
  LOCAL4   = 20 << 3,
  /// Local5 facility
  LOCAL5   = 21 << 3,
  /// Local6 facility
  LOCAL6   = 22 << 3,
  /// Local7 facility
  LOCAL7   = 23 << 3
}

/// Syslog [Severities](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  /// Emergency Severity
  EMERGENCY,
  /// Alert Severity
  ALERT,
  /// Critical Severity
  CRITICAL,
  /// Error Severity
  ERROR,
  /// Warning Severity
  WARNING,
  /// Notice Severity
  NOTICE,
  /// Info Severity
  INFO,
  /// Debug Severity
  DEBUG
}

/// Result of log operations
pub type Result = io::Result<()>;

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), &self.1).map(|_| ())
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_all(line.as_bytes())
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  /// A Syslog host entry as defined by
  /// [rfc5424#section-6.2.4](https://tools.ietf.org/html/rfc5424#section-6.2.4)
  host: Option<String>,
  /// An optional app-name appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.5](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  app: Option<String>,
  /// An optional proc-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.6](https://tools.ietf.org/html/rfc5424#section-6.2.6)
  pid: Option<String>,
  /// An optional msg-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.7](https://tools.ietf.org/html/rfc5424#section-6.2.7)
  msgid: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind("0.0.0.0:0") {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        app: None,
        pid: None,
        msgid: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 514)))
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix<P: AsRef<Path>>(path: P) -> Syslog {
    let stream =
      match UnixStream::connect(path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      app: None,
      pid: None,
      msgid: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided app-name
  pub fn app(self, app: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: Some(app.to_string()),
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided p(rocess)id
  pub fn pid(self, pid: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: Some(pid.to_string()),
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided msgid
  pub fn msgid(self, id: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: Some(id.to_string()),
      transport: self.transport
    }
  }

  /// Emits a debug level message
  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  /// Emits an info level message
  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  /// Emits an info level message
  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  /// Emits an warn level message
  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  /// Emits an error level message
  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  /// Emits a critical level message
  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  /// Emits an alert level message
  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  /// Emits a emergencycritical level message
  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(
        self.facility.clone(), severity, time::now(), self.host.clone(), self.app.clone(), self.pid.clone(), self.msgid.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, app: Option<String>, pid: Option<String>, msgid: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}>1 {} {} {} {} {} {}",
        Syslog::priority(facility, severity),
        timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()),
        app.unwrap_or(NIL.to_string()),
        pid.unwrap_or(NIL.to_string()),
        msgid.unwrap_or(NIL.to_string()),
        msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, Severity};
  use time;
  //use test::Bencher;

  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, None, "yo"),
      format!("<134>1 {} - - - - yo", ts.rfc3339()));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, None, None, "yo"),
      format!("<134>1 {} {} - - - yo", ts.rfc3339(), host));
  }

  #[test]
  fn test_syslog_line_app() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), None, None, "yo"),
      format!("<134>1 {} - {} - - yo", ts.rfc3339(), app));
  }

  #[test]
  fn test_syslog_line_pid() {
    let ts = time::now();
    let pid = "16";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, Some(pid.to_string()), None, "yo"),
      format!("<134>1 {} - - {} - yo", ts.rfc3339(), pid));
  }

  #[test]
  fn test_syslog_line_msgid() {
    let ts = time::now();
    let msgid = "TCPIN";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, Some(msgid.to_string()), "yo"),
      format!("<134>1 {} - - - {} yo", ts.rfc3339(), msgid));
  }

  //#[bench]
  //fn bench_assembly_line(b: &mut Bencher) {
  // b.iter(|| Syslog::line(
  //    Facility::LOCAL0, Severity::INFO, time::now(), None, None, None, None, "yo"))
  //}
}
<|endoftext|>


<commit_before>//! # rust-rdkafka
//! A fully asynchronous, [futures]-based Kafka client library for Rust based on [librdkafka].
//!
//! ## The library
//! `rust-rdkafka` provides a safe Rust interface to librdkafka. The master branch is currently based on librdkafka 0.11.3.
//!
//! ### Documentation
//!
//! - [Current master branch](https://fede1024.github.io/rust-rdkafka/)
//! - [Latest release](https://docs.rs/rdkafka/)
//! - [Changelog](https://github.com/fede1024/rust-rdkafka/blob/master/changelog.md)
//!
//! ### Features
//!
//! The main features provided at the moment are:
//!
//! - Support for all Kafka versions since 0.8.x. For more information about broker compatibility options, check the [librdkafka documentation].
//! - Consume from single or multiple topics.
//! - Automatic consumer rebalancing.
//! - Customizable rebalance, with pre and post rebalance callbacks.
//! - Synchronous or asynchronous message production.
//! - Customizable offset commit.
//! - Access to cluster metadata (list of topic-partitions, replicas, active brokers etc).
//! - Access to group metadata (list groups, list members of groups, hostnames etc).
//! - Access to producer and consumer metrics, errors and callbacks.
//!
//! [librdkafka documentation]: https://github.com/edenhill/librdkafka/wiki/Broker-version-compatibility
//!
//! ### One million messages per second
//!
//! `rust-rdkafka` is designed to be easy and safe to use thanks to the abstraction layer written in Rust, while at the same time being extremely fast thanks to the librdkafka C library.
//!
//! Here are some benchmark results using the rust-rdkafka `BaseProducer`, sending data to a single Kafka 0.11 process running in localhost (default configuration, 3 partitions). Hardware: Dell laptop, with Intel Core i7-4712HQ @ 2.30GHz.
//!
//! - Scenario: produce 5 million messages, 10 bytes each, wait for all of them to be acked
//!   - 1045413 messages/s, 9.970 MB/s  (average over 5 runs)
//!
//! - Scenario: produce 100000 messages, 10 KB each, wait for all of them to be acked
//!   - 24623 messages/s, 234.826 MB/s  (average over 5 runs)
//!
//! For more numbers, check out the [kafka-benchmark](https://github.com/fede1024/kafka-benchmark) project.
//!
//! ### Client types
//!
//! `rust-rdkafka` provides low level and high level consumers and producers. Low level:
//!
//! * [`BaseConsumer`]: simple wrapper around the librdkafka consumer. It requires to be periodically `poll()`ed in order to execute callbacks, rebalances and to receive messages.
//! * [`BaseProducer`]: simple wrapper around the librdkafka producer. As in the consumer case, the user must call `poll()` periodically to execute delivery callbacks.
//! * [`ThreadedProducer`]: `BaseProducer` with a separate thread dedicated to polling the producer.
//!
//! High level:
//!
//!  * [`StreamConsumer`]: it returns a [`stream`] of messages and takes care of polling the consumer internally.
//!  * [`FutureProducer`]: it returns a [`future`] that will be completed once the message is delivered to Kafka (or failed).
//!
//! For more information about consumers and producers, refer to their module-level documentation.
//!
//! [`BaseConsumer`]: https://docs.rs/rdkafka/*/rdkafka/consumer/base_consumer/struct.BaseConsumer.html
//! [`BaseProducer`]: https://docs.rs/rdkafka/*/rdkafka/producer/base_producer/struct.BaseProducer.html
//! [`ThreadedProducer`]: https://docs.rs/rdkafka/*/rdkafka/producer/base_producer/struct.ThreadedProducer.html
//! [`StreamConsumer`]: https://docs.rs/rdkafka/*/rdkafka/consumer/stream_consumer/struct.StreamConsumer.html
//! [`FutureProducer`]: https://docs.rs/rdkafka/*/rdkafka/producer/future_producer/struct.FutureProducer.html
//! [librdkafka]: https://github.com/edenhill/librdkafka
//! [futures]: https://github.com/alexcrichton/futures-rs
//! [`future`]: https://docs.rs/futures/0.1.3/futures/trait.Future.html
//! [`stream`]: https://docs.rs/futures/0.1.3/futures/stream/trait.Stream.html
//!
//! *Warning*: the library is under active development and the APIs are likely to change.
//!
//! ### Asynchronous data processing with tokio-rs
//! [tokio-rs] is a platform for fast processing of asynchronous events in Rust. The interfaces exposed by the [`StreamConsumer`] and the [`FutureProducer`] allow rust-rdkafka users to easily integrate Kafka consumers and producers within the tokio-rs platform, and write asynchronous message processing code. Note that rust-rdkafka can be used without tokio-rs.
//!
//! To see rust-rdkafka in action with tokio-rs, check out the [asynchronous processing example] in the examples folder.
//!
//! [tokio-rs]: https://tokio.rs/
//! [asynchronous processing example]: https://github.com/fede1024/rust-rdkafka/blob/master/examples/asynchronous_processing.rs
//!
//! ### At-least-once delivery
//!
//! At-least-once delivery semantic is common in many streaming applications: every message is guaranteed to be processed at least once; in case of temporary failure, the message can be re-processed and/or re-delivered, but no message will be lost.
//!
//! In order to implement at-least-once delivery the stream processing application has to carefully commit the offset only once the message has been processed. Committing the offset too early, instead, might cause message loss, since upon recovery the consumer will start from the next message, skipping the one where the failure occurred.
//!
//! To see how to implement at-least-once delivery with `rdkafka`, check out the [at-least-once delivery example] in the examples folder. To know more about delivery semantics, check the [message delivery semantics] chapter in the Kafka documentation.
//!
//! [at-least-once delivery example]: https://github.com/fede1024/rust-rdkafka/blob/master/examples/at_least_once.rs
//! [message delivery semantics]: https://kafka.apache.org/0101/documentation.html#semantics
//!
//! ### Users
//!
//! Here are some of the projects using rust-rdkafka:
//!
//! - [timely-dataflow]: a modular implementation of timely dataflow in Rust (you can also check the [blog post]).
//! - [kafka-view]: a web interface for Kafka clusters.
//! - [kafka-benchmark]: a high performance benchmarking tool for Kafka.
//!
//! *If you are using rust-rdkafka, please let me know!*
//!
//! [timely-dataflow]: https://github.com/frankmcsherry/timely-dataflow
//! [kafka-view]: https://github.com/fede1024/kafka-view
//! [kafka-benchmark]: https://github.com/fede1024/kafka-benchmark
//! [blog post]: https://github.com/frankmcsherry/blog/blob/master/posts/2017-11-08.md
//!
//! ## Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rdkafka = "0.15.0"
//! ```
//!
//! This crate will compile librdkafka from sources and link it statically to your executable. To compile librdkafka you'll need:
//!
//! * the GNU toolchain
//! * GNU `make`
//! * `pthreads`
//! * `zlib`
//! * `libssl-dev`: optional, *not* included by default (feature: `ssl`).
//! * `libsasl2-dev`: optional, *not* included by default (feature: `sasl`).
//!
//! To enable ssl and sasl, use the `features` field in `Cargo.toml`. Example:
//!
//! ```toml
//! [dependencies.rdkafka]
//! version = "0.15.0"
//! features = ["ssl", "sasl"]
//! ```
//!
//! By default a submodule with the librdkafka sources pinned to a specific commit will
//! be used to compile and statically link the library.
//!
//! The `dynamic_linking` feature can be used to link rdkafka to a locally installed
//! version of librdkafka: if the feature is enabled, the build script will use `pkg-config`
//! to check the version of the library installed in the system, and it will configure the
//! compiler to use dynamic linking.
//!
//! ## Compiling from sources
//!
//! To compile from sources, you'll have to update the submodule containing librdkafka:
//!
//! ```bash
//! git submodule update --init
//! ```
//!
//! and then compile using `cargo`, selecting the features that you want. Example:
//!
//! ```bash
//! cargo build --features "ssl sasl"
//! ```
//!
//! ## Examples
//!
//! You can find examples in the `examples` folder. To run them:
//!
//! ```bash
//! cargo run --example <example_name> -- <example_args>
//! ```
//!
//! ## Tests
//!
//! ### Unit tests
//!
//! The unit tests can run without a Kafka broker present:
//!
//! ```bash
//! cargo test --lib
//! ```
//!
//! ### Automatic testing
//!
//! rust-rdkafka contains a suite of tests which is automatically executed by travis in
//! docker-compose. Given the interaction with C code that rust-rdkafka has to do, tests
//! are executed in valgrind to check eventual memory errors and leaks.
//!
//! To run the full suite using docker-compose:
//!
//! ```bash
//! ./test_suite.sh
//! ```
//!
//! To run locally, instead:
//!
//! ```bash
//! KAFKA_HOST="kafka_server:9092" cargo test
//! ```
//!
//! In this case there is a broker expected to be running on `KAFKA_HOST`.
//! The broker must be configured with default partition number 3 and topic autocreation in order
//! for the tests to succeed.
//!
//! ## Debugging
//!
//! rust-rdkafka uses the `log` and `env_logger` crates to handle logging. Logging can be enabled
//! using the `RUST_LOG` environment variable, for example:
//!
//! ```bash
//! RUST_LOG="librdkafka=trace,rdkafka::client=debug" cargo test
//! ```
//!
//! This will configure the logging level of librdkafka to trace, and the level of the client
//! module of the Rust client to debug. To actually receive logs from librdkafka, you also have to
//! set the `debug` option in the producer or consumer configuration (see librdkafka
//! [configuration](https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md)).
//!
//! To enable debugging in your project, make sure you initialize the logger with
//! `env_logger::init()` or equivalent.
//!

#![warn(missing_docs)]

//>alloc_system

//#![feature(alloc_system, global_allocator, allocator_api)]
//extern crate alloc_system;
//use alloc_system::System;
//#[global_allocator]
//static A: System = System;

#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate futures;

extern crate rdkafka_sys as rdsys;

pub use rdsys::types as types;

pub mod client;
pub mod config;
pub mod consumer;
pub mod error;
pub mod groups;
pub mod message;
pub mod metadata;
pub mod producer;
pub mod statistics;
pub mod topic_partition_list;
pub mod util;

// Re-export
pub use client::ClientContext;
pub use config::ClientConfig;
pub use message::{Message, Timestamp};
pub use statistics::Statistics;
pub use topic_partition_list::{Offset, TopicPartitionList};
pub use util::IntoOpaque;
<commit_msg>Make documentation links relative if possible<commit_after>//! # rust-rdkafka
//! A fully asynchronous, [futures]-based Kafka client library for Rust based on [librdkafka].
//!
//! ## The library
//! `rust-rdkafka` provides a safe Rust interface to librdkafka. The master branch is currently based on librdkafka 0.11.3.
//!
//! ### Documentation
//!
//! - [Current master branch](https://fede1024.github.io/rust-rdkafka/)
//! - [Latest release](https://docs.rs/rdkafka/)
//! - [Changelog](https://github.com/fede1024/rust-rdkafka/blob/master/changelog.md)
//!
//! ### Features
//!
//! The main features provided at the moment are:
//!
//! - Support for all Kafka versions since 0.8.x. For more information about broker compatibility options, check the [librdkafka documentation].
//! - Consume from single or multiple topics.
//! - Automatic consumer rebalancing.
//! - Customizable rebalance, with pre and post rebalance callbacks.
//! - Synchronous or asynchronous message production.
//! - Customizable offset commit.
//! - Access to cluster metadata (list of topic-partitions, replicas, active brokers etc).
//! - Access to group metadata (list groups, list members of groups, hostnames etc).
//! - Access to producer and consumer metrics, errors and callbacks.
//!
//! [librdkafka documentation]: https://github.com/edenhill/librdkafka/wiki/Broker-version-compatibility
//!
//! ### One million messages per second
//!
//! `rust-rdkafka` is designed to be easy and safe to use thanks to the abstraction layer written in Rust, while at the same time being extremely fast thanks to the librdkafka C library.
//!
//! Here are some benchmark results using the rust-rdkafka `BaseProducer`, sending data to a single Kafka 0.11 process running in localhost (default configuration, 3 partitions). Hardware: Dell laptop, with Intel Core i7-4712HQ @ 2.30GHz.
//!
//! - Scenario: produce 5 million messages, 10 bytes each, wait for all of them to be acked
//!   - 1045413 messages/s, 9.970 MB/s  (average over 5 runs)
//!
//! - Scenario: produce 100000 messages, 10 KB each, wait for all of them to be acked
//!   - 24623 messages/s, 234.826 MB/s  (average over 5 runs)
//!
//! For more numbers, check out the [kafka-benchmark](https://github.com/fede1024/kafka-benchmark) project.
//!
//! ### Client types
//!
//! `rust-rdkafka` provides low level and high level consumers and producers. Low level:
//!
//! * [`BaseConsumer`]: simple wrapper around the librdkafka consumer. It requires to be periodically `poll()`ed in order to execute callbacks, rebalances and to receive messages.
//! * [`BaseProducer`]: simple wrapper around the librdkafka producer. As in the consumer case, the user must call `poll()` periodically to execute delivery callbacks.
//! * [`ThreadedProducer`]: `BaseProducer` with a separate thread dedicated to polling the producer.
//!
//! High level:
//!
//!  * [`StreamConsumer`]: it returns a [`stream`] of messages and takes care of polling the consumer internally.
//!  * [`FutureProducer`]: it returns a [`future`] that will be completed once the message is delivered to Kafka (or failed).
//!
//! For more information about consumers and producers, refer to their module-level documentation.
//!
//! [`BaseConsumer`]: consumer/base_consumer/struct.BaseConsumer.html
//! [`BaseProducer`]: producer/base_producer/struct.BaseProducer.html
//! [`ThreadedProducer`]: producer/base_producer/struct.ThreadedProducer.html
//! [`StreamConsumer`]: consumer/stream_consumer/struct.StreamConsumer.html
//! [`FutureProducer`]: producer/future_producer/struct.FutureProducer.html
//! [librdkafka]: https://github.com/edenhill/librdkafka
//! [futures]: https://github.com/alexcrichton/futures-rs
//! [`future`]: https://docs.rs/futures/0.1.3/futures/trait.Future.html
//! [`stream`]: https://docs.rs/futures/0.1.3/futures/stream/trait.Stream.html
//!
//! *Warning*: the library is under active development and the APIs are likely to change.
//!
//! ### Asynchronous data processing with tokio-rs
//! [tokio-rs] is a platform for fast processing of asynchronous events in Rust. The interfaces exposed by the [`StreamConsumer`] and the [`FutureProducer`] allow rust-rdkafka users to easily integrate Kafka consumers and producers within the tokio-rs platform, and write asynchronous message processing code. Note that rust-rdkafka can be used without tokio-rs.
//!
//! To see rust-rdkafka in action with tokio-rs, check out the [asynchronous processing example] in the examples folder.
//!
//! [tokio-rs]: https://tokio.rs/
//! [asynchronous processing example]: https://github.com/fede1024/rust-rdkafka/blob/master/examples/asynchronous_processing.rs
//!
//! ### At-least-once delivery
//!
//! At-least-once delivery semantic is common in many streaming applications: every message is guaranteed to be processed at least once; in case of temporary failure, the message can be re-processed and/or re-delivered, but no message will be lost.
//!
//! In order to implement at-least-once delivery the stream processing application has to carefully commit the offset only once the message has been processed. Committing the offset too early, instead, might cause message loss, since upon recovery the consumer will start from the next message, skipping the one where the failure occurred.
//!
//! To see how to implement at-least-once delivery with `rdkafka`, check out the [at-least-once delivery example] in the examples folder. To know more about delivery semantics, check the [message delivery semantics] chapter in the Kafka documentation.
//!
//! [at-least-once delivery example]: https://github.com/fede1024/rust-rdkafka/blob/master/examples/at_least_once.rs
//! [message delivery semantics]: https://kafka.apache.org/0101/documentation.html#semantics
//!
//! ### Users
//!
//! Here are some of the projects using rust-rdkafka:
//!
//! - [timely-dataflow]: a modular implementation of timely dataflow in Rust (you can also check the [blog post]).
//! - [kafka-view]: a web interface for Kafka clusters.
//! - [kafka-benchmark]: a high performance benchmarking tool for Kafka.
//!
//! *If you are using rust-rdkafka, please let me know!*
//!
//! [timely-dataflow]: https://github.com/frankmcsherry/timely-dataflow
//! [kafka-view]: https://github.com/fede1024/kafka-view
//! [kafka-benchmark]: https://github.com/fede1024/kafka-benchmark
//! [blog post]: https://github.com/frankmcsherry/blog/blob/master/posts/2017-11-08.md
//!
//! ## Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rdkafka = "0.15.0"
//! ```
//!
//! This crate will compile librdkafka from sources and link it statically to your executable. To compile librdkafka you'll need:
//!
//! * the GNU toolchain
//! * GNU `make`
//! * `pthreads`
//! * `zlib`
//! * `libssl-dev`: optional, *not* included by default (feature: `ssl`).
//! * `libsasl2-dev`: optional, *not* included by default (feature: `sasl`).
//!
//! To enable ssl and sasl, use the `features` field in `Cargo.toml`. Example:
//!
//! ```toml
//! [dependencies.rdkafka]
//! version = "0.15.0"
//! features = ["ssl", "sasl"]
//! ```
//!
//! By default a submodule with the librdkafka sources pinned to a specific commit will
//! be used to compile and statically link the library.
//!
//! The `dynamic_linking` feature can be used to link rdkafka to a locally installed
//! version of librdkafka: if the feature is enabled, the build script will use `pkg-config`
//! to check the version of the library installed in the system, and it will configure the
//! compiler to use dynamic linking.
//!
//! ## Compiling from sources
//!
//! To compile from sources, you'll have to update the submodule containing librdkafka:
//!
//! ```bash
//! git submodule update --init
//! ```
//!
//! and then compile using `cargo`, selecting the features that you want. Example:
//!
//! ```bash
//! cargo build --features "ssl sasl"
//! ```
//!
//! ## Examples
//!
//! You can find examples in the `examples` folder. To run them:
//!
//! ```bash
//! cargo run --example <example_name> -- <example_args>
//! ```
//!
//! ## Tests
//!
//! ### Unit tests
//!
//! The unit tests can run without a Kafka broker present:
//!
//! ```bash
//! cargo test --lib
//! ```
//!
//! ### Automatic testing
//!
//! rust-rdkafka contains a suite of tests which is automatically executed by travis in
//! docker-compose. Given the interaction with C code that rust-rdkafka has to do, tests
//! are executed in valgrind to check eventual memory errors and leaks.
//!
//! To run the full suite using docker-compose:
//!
//! ```bash
//! ./test_suite.sh
//! ```
//!
//! To run locally, instead:
//!
//! ```bash
//! KAFKA_HOST="kafka_server:9092" cargo test
//! ```
//!
//! In this case there is a broker expected to be running on `KAFKA_HOST`.
//! The broker must be configured with default partition number 3 and topic autocreation in order
//! for the tests to succeed.
//!
//! ## Debugging
//!
//! rust-rdkafka uses the `log` and `env_logger` crates to handle logging. Logging can be enabled
//! using the `RUST_LOG` environment variable, for example:
//!
//! ```bash
//! RUST_LOG="librdkafka=trace,rdkafka::client=debug" cargo test
//! ```
//!
//! This will configure the logging level of librdkafka to trace, and the level of the client
//! module of the Rust client to debug. To actually receive logs from librdkafka, you also have to
//! set the `debug` option in the producer or consumer configuration (see librdkafka
//! [configuration](https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md)).
//!
//! To enable debugging in your project, make sure you initialize the logger with
//! `env_logger::init()` or equivalent.
//!

#![warn(missing_docs)]

//>alloc_system

//#![feature(alloc_system, global_allocator, allocator_api)]
//extern crate alloc_system;
//use alloc_system::System;
//#[global_allocator]
//static A: System = System;

#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate futures;

extern crate rdkafka_sys as rdsys;

pub use rdsys::types as types;

pub mod client;
pub mod config;
pub mod consumer;
pub mod error;
pub mod groups;
pub mod message;
pub mod metadata;
pub mod producer;
pub mod statistics;
pub mod topic_partition_list;
pub mod util;

// Re-export
pub use client::ClientContext;
pub use config::ClientConfig;
pub use message::{Message, Timestamp};
pub use statistics::Statistics;
pub use topic_partition_list::{Offset, TopicPartitionList};
pub use util::IntoOpaque;
<|endoftext|>


<commit_before>#![allow(dead_code)]

pub type Error = Box<::std::error::Error>;
pub type Result<T> = ::std::result::Result<T, Error>;

pub struct Promise<T> {
    phantom_data : ::std::marker::PhantomData<T>,
    pub node : Box<PromiseNode>,
}

pub trait Event {
    fn fire(&mut self);
}

pub trait PromiseNode {
    /// Arms the given event when ready.
    fn on_ready(&self, event : &mut Event);

    fn set_self_pointer(&mut self) {}
    fn get(&self);
}

/// A promise that has already been resolved to an immediate value or error.
pub struct ImmediatePromiseNode<T> {
    result : ::std::result::Result<T, Error>,
}

impl <T> PromiseNode for ImmediatePromiseNode<T> {
    fn on_ready(&self, event : &mut Event) {
        unimplemented!();
        //event.arm_breadth_first();
    }
    fn get(&self) {

    }
}

trait ErrorHandler {
    fn task_failed(error: Error);
}

struct TaskSetImpl {
    error_handler: Box<ErrorHandler>,
}

/// A Deque with an insertion point in the middle.
struct InsertDeque<T> {
    front: ::std::collections::VecDeque<T>,
    back: ::std::collections::VecDeque<T>,
}

/// A queue of events being executed in a loop.
struct EventLoop {
    daemons: TaskSetImpl,
    running: bool,
    last_runnable_state: bool,
    events: ::std::collections::VecDeque<Box<Event>>,
    depth_first_events: ::std::collections::VecDeque<Box<Event>>,
}

impl EventLoop {

    /// Run the even loop for `max_turn_count` turns or until there is nothing left to be done,
    /// whichever comes first. This never calls the `EventPort`'s `sleep()` or `poll()`. It will
    /// call the `EventPort`'s `set_runnable(false)` if the queue becomes empty.
    pub fn run(&mut self, max_turn_count : u32) {
        self.running = true;

        for _ in 0..max_turn_count {
            if !self.turn() {
                break;
            }
        }
    }

    fn turn(&mut self) -> bool {
        assert!(self.depth_first_events.is_empty());
        match self.events.pop_front() {
            None => return false,
            Some(mut event) => {
                // event->firing = true ?
                event.fire();
            }
        }
        while(!self.depth_first_events.is_empty()) {
            self.events.push_front(self.depth_first_events.pop_back().unwrap());
        }
        return true;
    }
}
<commit_msg>thread local current event loop<commit_after>#![allow(dead_code)]

use std::cell::RefCell;

thread_local!(static EVENT_LOOP: Option<RefCell<EventLoop>> = None);

pub fn with_current_event_loop<F, R>(f: F) -> R
    where F: FnOnce(&RefCell<EventLoop>) -> R {
        EVENT_LOOP.with(|maybe_event_loop| {
            match maybe_event_loop {
                &None => panic!("current thread has no event loop"),
                &Some(ref event_loop) => f(event_loop),
            }
        })
    }

pub type Error = Box<::std::error::Error>;
pub type Result<T> = ::std::result::Result<T, Error>;

pub struct Promise<T> {
    phantom_data : ::std::marker::PhantomData<T>,
    pub node : Box<PromiseNode>,
}

pub trait Event {
    fn fire(&mut self);
}

pub trait PromiseNode {
    /// Arms the given event when ready.
    fn on_ready(&self, event : Box<Event>);

    fn set_self_pointer(&mut self) {}
    fn get(&self);
}

/// A promise that has already been resolved to an immediate value or error.
pub struct ImmediatePromiseNode<T> {
    result : ::std::result::Result<T, Error>,
}

impl <T> PromiseNode for ImmediatePromiseNode<T> {

    /// Arms the event then the promise is ready.
    fn on_ready(&self, event: Box<Event>) {
        with_current_event_loop(|event_loop| {
            event_loop.borrow_mut().arm_breadth_first(event);
        });
    }
    fn get(&self) {

    }
}

trait ErrorHandler {
    fn task_failed(error: Error);
}

struct TaskSetImpl {
    error_handler: Box<ErrorHandler>,
}

/// A Deque with an insertion point in the middle.
struct InsertDeque<T> {
    front: ::std::collections::VecDeque<T>,
    back: ::std::collections::VecDeque<T>,
}

/// A queue of events being executed in a loop.
pub struct EventLoop {
    daemons: TaskSetImpl,
    running: bool,
    last_runnable_state: bool,
    events: ::std::collections::VecDeque<Box<Event>>,
    depth_first_events: ::std::collections::VecDeque<Box<Event>>,
}

impl EventLoop {

    fn arm_depth_first(&mut self, event: Box<Event>) {
        self.depth_first_events.push_front(event);
    }

    fn arm_breadth_first(&mut self, event: Box<Event>) {
        self.events.push_back(event);
    }

    /// Run the even loop for `max_turn_count` turns or until there is nothing left to be done,
    /// whichever comes first. This never calls the `EventPort`'s `sleep()` or `poll()`. It will
    /// call the `EventPort`'s `set_runnable(false)` if the queue becomes empty.
    pub fn run(&mut self, max_turn_count : u32) {
        self.running = true;

        for _ in 0..max_turn_count {
            if !self.turn() {
                break;
            }
        }
    }

    fn turn(&mut self) -> bool {
        assert!(self.depth_first_events.is_empty());
        match self.events.pop_front() {
            None => return false,
            Some(mut event) => {
                // event->firing = true ?
                event.fire();
            }
        }
        while !self.depth_first_events.is_empty() {
            self.events.push_front(self.depth_first_events.pop_back().unwrap());
        }
        return true;
    }
}
<|endoftext|>