<commit_before>use crate::config::Config;
use crate::series::SeriesPath;
use crate::try_opt_ret;
use crate::tui::component::Draw;
use crate::tui::widget_util::{block, style};
use crate::{SERIES_EPISODE_REP, SERIES_TITLE_REP};
use anime::local::EpisodeParser;
use std::borrow::Cow;
use std::path::PathBuf;
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::Rect;
use tui::style::Color;
use tui::terminal::Frame;
use tui::widgets::{Paragraph, Text};
use unicode_width::UnicodeWidthChar;

pub struct Input {
    caret: Caret,
    visible_rect: Rect,
    offset: usize,
    pub selected: bool,
    pub error: bool,
}

impl Input {
    const BORDER_SIZE: u16 = 2;

    pub fn new(selected: bool) -> Self {
        Self {
            caret: Caret::new(),
            visible_rect: Rect::default(),
            offset: 0,
            selected,
            error: false,
        }
    }

    pub fn process_key(&mut self, key: Key) {
        match key {
            Key::Char(ch) => self.caret.push(ch),
            Key::Backspace => self.caret.pop(),
            Key::Left => self.caret.move_left(),
            Key::Right => self.caret.move_right(),
            Key::Home => self.caret.move_front(),
            Key::End => self.caret.move_end(),
            _ => (),
        }

        self.update_visible_slice();
    }

    fn update_visible_slice(&mut self) {
        let max_width = self.visible_rect.width - Self::BORDER_SIZE - 1;

        if self.caret.pos < max_width as usize {
            self.offset = 0;
            return;
        }

        self.offset = self.caret.pos - max_width as usize;
    }

    pub fn calculate_cursor_pos(column: u16, rect: Rect) -> (u16, u16) {
        let rect_width = rect.width.saturating_sub(Self::BORDER_SIZE);

        let (len, line_num) = if rect_width > 0 {
            let line_num = column / rect_width;
            let max_line = rect.height - Self::BORDER_SIZE - 1;

            // We want to cap the position of the cursor to the last character of the last line
            if line_num > max_line {
                (rect_width - 1, max_line)
            } else {
                (column % rect_width, line_num)
            }
        } else {
            (column, 0)
        };

        let x = 1 + rect.left() + len;
        let y = 1 + rect.top() + line_num;

        (x, y)
    }

    #[inline(always)]
    pub fn will_cursor_fit(rect: Rect) -> bool {
        rect.height > Self::BORDER_SIZE && rect.width > Self::BORDER_SIZE
    }

    fn set_cursor_pos<B>(&self, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        if !self.selected || !Self::will_cursor_fit(rect) {
            return;
        }

        let width = (self.caret.cur_width as u16).min(rect.width);
        let (x, y) = Self::calculate_cursor_pos(width, rect);

        frame.set_cursor(x, y);
    }

    pub fn clear(&mut self) {
        self.caret.clear();
        self.offset = 0;
    }

    #[inline(always)]
    pub fn text(&self) -> &str {
        &self.caret.buffer
    }

    #[inline(always)]
    pub fn visible(&self) -> &str {
        &self.caret.buffer[self.offset..]
    }
}

impl<B> Draw<B> for Input
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        self.visible_rect = rect;

        let block_color = match (self.selected, self.error) {
            (true, true) => Some(Color::LightRed),
            (true, false) => Some(Color::Blue),
            (false, true) => Some(Color::Red),
            (false, false) => None,
        };

        let mut block = block::with_borders(None);

        if let Some(color) = block_color {
            block = block.border_style(style::fg(color));
        }

        let text = [Text::raw(self.visible())];
        let widget = Paragraph::new(text.iter()).block(block).wrap(false);

        frame.render_widget(widget, rect);
        self.set_cursor_pos(rect, frame);
    }
}

struct Caret {
    buffer: String,
    cur_width: usize,
    total_width: usize,
    pos: usize,
}

impl Caret {
    fn new() -> Self {
        Self {
            buffer: String::new(),
            cur_width: 0,
            total_width: 0,
            pos: 0,
        }
    }

    fn push(&mut self, ch: char) {
        self.buffer.insert(self.cur_width, ch);

        let width = UnicodeWidthChar::width(ch).unwrap_or(0);

        self.cur_width += width;
        self.total_width += width;
        self.pos += 1;
    }

    fn pop(&mut self) {
        if self.buffer.is_empty() || self.pos == 0 {
            return;
        }

        let ch = if self.pos == self.buffer.len() {
            try_opt_ret!(self.buffer.pop())
        } else {
            self.buffer.remove(self.pos - 1)
        };

        let width = UnicodeWidthChar::width(ch).unwrap_or(0);

        self.cur_width -= width;
        self.total_width -= width;
        self.pos -= 1;
    }

    fn move_left(&mut self) {
        if self.pos == 0 {
            return;
        }

        let left_char = try_opt_ret!(self.buffer.chars().nth(self.pos - 1));

        self.cur_width -= UnicodeWidthChar::width(left_char).unwrap_or(0);
        self.pos -= 1;
    }

    fn move_right(&mut self) {
        if self.pos > self.buffer.len() {
            return;
        }

        let right_char = try_opt_ret!(self.buffer.chars().nth(self.pos));

        self.cur_width += UnicodeWidthChar::width(right_char).unwrap_or(0);
        self.pos += 1;
    }

    fn move_front(&mut self) {
        self.cur_width = 0;
        self.pos = 0;
    }

    fn move_end(&mut self) {
        self.cur_width = self.total_width;
        self.pos = self.buffer.len();
    }

    fn clear(&mut self) {
        self.buffer.clear();
        self.cur_width = 0;
        self.total_width = 0;
        self.pos = 0;
    }
}

pub trait ValidatedInput {
    fn label(&self) -> &'static str;
    fn input_mut(&mut self) -> &mut Input;

    fn validate(&mut self);

    fn has_error(&self) -> bool;
    fn error_message(&self) -> Cow<'static, str>;

    fn error(&self) -> Option<Cow<'static, str>> {
        if self.has_error() {
            Some(self.error_message())
        } else {
            None
        }
    }
}

pub trait ParsedValue {
    type Value: ?Sized;

    fn parsed_value(&self) -> &Self::Value;
}

macro_rules! impl_draw_for_input {
    ($struct:ident) => {
        impl<B> Draw<B> for $struct
        where
            B: Backend,
        {
            type State = ();

            fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
                self.input_mut().draw(&(), rect, frame)
            }
        }
    };
}

pub struct NameInput(Input);

impl NameInput {
    #[inline(always)]
    pub fn new(selected: bool) -> Self {
        Self(Input::new(selected))
    }
}

impl ValidatedInput for NameInput {
    fn label(&self) -> &'static str {
        "Name"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.0
    }

    fn validate(&mut self) {
        let empty = self.0.text().is_empty();
        self.0.error = empty;
    }

    fn has_error(&self) -> bool {
        self.0.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "Name must not be empty".into()
    }
}

impl ParsedValue for NameInput {
    type Value = str;

    fn parsed_value(&self) -> &Self::Value {
        self.0.text()
    }
}

impl_draw_for_input!(NameInput);

pub struct IDInput {
    input: Input,
    id: Option<i32>,
}

impl IDInput {
    pub fn new(selected: bool) -> Self {
        Self {
            input: Input::new(selected),
            id: None,
        }
    }
}

impl ValidatedInput for IDInput {
    fn label(&self) -> &'static str {
        "ID"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.id = None;
            self.input.error = false;
            return;
        }

        let (result, error) = match text.parse() {
            Ok(num) if num >= 0 => (Some(num), false),
            _ => (None, true),
        };

        self.id = result;
        self.input.error = error;
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "ID must be a positive number".into()
    }
}

impl ParsedValue for IDInput {
    type Value = Option<i32>;

    fn parsed_value(&self) -> &Self::Value {
        &self.id
    }
}

impl_draw_for_input!(IDInput);

pub struct PathInput {
    input: Input,
    base_path: PathBuf,
    path: Option<SeriesPath>,
}

impl PathInput {
    pub fn new(selected: bool, config: &Config) -> Self {
        Self {
            input: Input::new(selected),
            base_path: config.series_dir.clone(),
            path: None,
        }
    }
}

impl ValidatedInput for PathInput {
    fn label(&self) -> &'static str {
        "Path"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.path = None;
            self.input.error = false;
            return;
        }

        let path = SeriesPath::with_base(&self.base_path, Cow::Owned(PathBuf::from(text)));
        let exists = path.exists_base(&self.base_path);

        self.path = if exists { Some(path) } else { None };
        self.input.error = !exists;
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "Path must exist".into()
    }
}

impl ParsedValue for PathInput {
    type Value = Option<SeriesPath>;

    fn parsed_value(&self) -> &Self::Value {
        &self.path
    }
}

impl_draw_for_input!(PathInput);

pub struct ParserInput {
    input: Input,
    parser: EpisodeParser,
}

impl ParserInput {
    pub fn new(selected: bool) -> Self {
        Self {
            input: Input::new(selected),
            parser: EpisodeParser::default(),
        }
    }
}

impl ValidatedInput for ParserInput {
    fn label(&self) -> &'static str {
        "Episode Regex"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.parser = EpisodeParser::default();
            self.input.error = false;
            return;
        }

        let parser =
            EpisodeParser::custom_with_replacements(text, SERIES_TITLE_REP, SERIES_EPISODE_REP)
                .ok();

        self.input.error = parser.is_none();
        self.parser = parser.unwrap_or_else(Default::default);
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        // TODO: use concat! macro if/when it can accept constants, or when a similiar crate doesn't require nightly
        format!(
            "Regex must contain \"{}\" and be valid",
            crate::SERIES_EPISODE_REP
        )
        .into()
    }
}

impl ParsedValue for ParserInput {
    type Value = EpisodeParser;

    fn parsed_value(&self) -> &Self::Value {
        &self.parser
    }
}

impl_draw_for_input!(ParserInput);
<commit_msg>anup: Only store visible width in input component<commit_after>use crate::config::Config;
use crate::series::SeriesPath;
use crate::try_opt_ret;
use crate::tui::component::Draw;
use crate::tui::widget_util::{block, style};
use crate::{SERIES_EPISODE_REP, SERIES_TITLE_REP};
use anime::local::EpisodeParser;
use std::borrow::Cow;
use std::path::PathBuf;
use termion::event::Key;
use tui::backend::Backend;
use tui::layout::Rect;
use tui::style::Color;
use tui::terminal::Frame;
use tui::widgets::{Paragraph, Text};
use unicode_width::UnicodeWidthChar;

pub struct Input {
    caret: Caret,
    visible_width: u16,
    offset: usize,
    pub selected: bool,
    pub error: bool,
}

impl Input {
    const BORDER_SIZE: u16 = 2;

    pub fn new(selected: bool) -> Self {
        Self {
            caret: Caret::new(),
            visible_width: 0,
            offset: 0,
            selected,
            error: false,
        }
    }

    pub fn process_key(&mut self, key: Key) {
        match key {
            Key::Char(ch) => self.caret.push(ch),
            Key::Backspace => self.caret.pop(),
            Key::Left => self.caret.move_left(),
            Key::Right => self.caret.move_right(),
            Key::Home => self.caret.move_front(),
            Key::End => self.caret.move_end(),
            _ => (),
        }

        self.update_visible_slice();
    }

    fn update_visible_slice(&mut self) {
        let max_width = self.visible_width - Self::BORDER_SIZE - 1;

        if self.caret.pos < max_width as usize {
            self.offset = 0;
            return;
        }

        self.offset = self.caret.pos - max_width as usize;
    }

    pub fn calculate_cursor_pos(column: u16, rect: Rect) -> (u16, u16) {
        let rect_width = rect.width.saturating_sub(Self::BORDER_SIZE);

        let (len, line_num) = if rect_width > 0 {
            let line_num = column / rect_width;
            let max_line = rect.height - Self::BORDER_SIZE - 1;

            // We want to cap the position of the cursor to the last character of the last line
            if line_num > max_line {
                (rect_width - 1, max_line)
            } else {
                (column % rect_width, line_num)
            }
        } else {
            (column, 0)
        };

        let x = 1 + rect.left() + len;
        let y = 1 + rect.top() + line_num;

        (x, y)
    }

    #[inline(always)]
    pub fn will_cursor_fit(rect: Rect) -> bool {
        rect.height > Self::BORDER_SIZE && rect.width > Self::BORDER_SIZE
    }

    fn set_cursor_pos<B>(&self, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        if !self.selected || !Self::will_cursor_fit(rect) {
            return;
        }

        let width = (self.caret.cur_width as u16).min(rect.width);
        let (x, y) = Self::calculate_cursor_pos(width, rect);

        frame.set_cursor(x, y);
    }

    pub fn clear(&mut self) {
        self.caret.clear();
        self.offset = 0;
    }

    #[inline(always)]
    pub fn text(&self) -> &str {
        &self.caret.buffer
    }

    #[inline(always)]
    pub fn visible(&self) -> &str {
        &self.caret.buffer[self.offset..]
    }
}

impl<B> Draw<B> for Input
where
    B: Backend,
{
    type State = ();

    fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
        self.visible_width = rect.width;

        let block_color = match (self.selected, self.error) {
            (true, true) => Some(Color::LightRed),
            (true, false) => Some(Color::Blue),
            (false, true) => Some(Color::Red),
            (false, false) => None,
        };

        let mut block = block::with_borders(None);

        if let Some(color) = block_color {
            block = block.border_style(style::fg(color));
        }

        let text = [Text::raw(self.visible())];
        let widget = Paragraph::new(text.iter()).block(block).wrap(false);

        frame.render_widget(widget, rect);
        self.set_cursor_pos(rect, frame);
    }
}

struct Caret {
    buffer: String,
    cur_width: usize,
    total_width: usize,
    pos: usize,
}

impl Caret {
    fn new() -> Self {
        Self {
            buffer: String::new(),
            cur_width: 0,
            total_width: 0,
            pos: 0,
        }
    }

    fn push(&mut self, ch: char) {
        self.buffer.insert(self.cur_width, ch);

        let width = UnicodeWidthChar::width(ch).unwrap_or(0);

        self.cur_width += width;
        self.total_width += width;
        self.pos += 1;
    }

    fn pop(&mut self) {
        if self.buffer.is_empty() || self.pos == 0 {
            return;
        }

        let ch = if self.pos == self.buffer.len() {
            try_opt_ret!(self.buffer.pop())
        } else {
            self.buffer.remove(self.pos - 1)
        };

        let width = UnicodeWidthChar::width(ch).unwrap_or(0);

        self.cur_width -= width;
        self.total_width -= width;
        self.pos -= 1;
    }

    fn move_left(&mut self) {
        if self.pos == 0 {
            return;
        }

        let left_char = try_opt_ret!(self.buffer.chars().nth(self.pos - 1));

        self.cur_width -= UnicodeWidthChar::width(left_char).unwrap_or(0);
        self.pos -= 1;
    }

    fn move_right(&mut self) {
        if self.pos > self.buffer.len() {
            return;
        }

        let right_char = try_opt_ret!(self.buffer.chars().nth(self.pos));

        self.cur_width += UnicodeWidthChar::width(right_char).unwrap_or(0);
        self.pos += 1;
    }

    fn move_front(&mut self) {
        self.cur_width = 0;
        self.pos = 0;
    }

    fn move_end(&mut self) {
        self.cur_width = self.total_width;
        self.pos = self.buffer.len();
    }

    fn clear(&mut self) {
        self.buffer.clear();
        self.cur_width = 0;
        self.total_width = 0;
        self.pos = 0;
    }
}

pub trait ValidatedInput {
    fn label(&self) -> &'static str;
    fn input_mut(&mut self) -> &mut Input;

    fn validate(&mut self);

    fn has_error(&self) -> bool;
    fn error_message(&self) -> Cow<'static, str>;

    fn error(&self) -> Option<Cow<'static, str>> {
        if self.has_error() {
            Some(self.error_message())
        } else {
            None
        }
    }
}

pub trait ParsedValue {
    type Value: ?Sized;

    fn parsed_value(&self) -> &Self::Value;
}

macro_rules! impl_draw_for_input {
    ($struct:ident) => {
        impl<B> Draw<B> for $struct
        where
            B: Backend,
        {
            type State = ();

            fn draw(&mut self, _: &Self::State, rect: Rect, frame: &mut Frame<B>) {
                self.input_mut().draw(&(), rect, frame)
            }
        }
    };
}

pub struct NameInput(Input);

impl NameInput {
    #[inline(always)]
    pub fn new(selected: bool) -> Self {
        Self(Input::new(selected))
    }
}

impl ValidatedInput for NameInput {
    fn label(&self) -> &'static str {
        "Name"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.0
    }

    fn validate(&mut self) {
        let empty = self.0.text().is_empty();
        self.0.error = empty;
    }

    fn has_error(&self) -> bool {
        self.0.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "Name must not be empty".into()
    }
}

impl ParsedValue for NameInput {
    type Value = str;

    fn parsed_value(&self) -> &Self::Value {
        self.0.text()
    }
}

impl_draw_for_input!(NameInput);

pub struct IDInput {
    input: Input,
    id: Option<i32>,
}

impl IDInput {
    pub fn new(selected: bool) -> Self {
        Self {
            input: Input::new(selected),
            id: None,
        }
    }
}

impl ValidatedInput for IDInput {
    fn label(&self) -> &'static str {
        "ID"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.id = None;
            self.input.error = false;
            return;
        }

        let (result, error) = match text.parse() {
            Ok(num) if num >= 0 => (Some(num), false),
            _ => (None, true),
        };

        self.id = result;
        self.input.error = error;
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "ID must be a positive number".into()
    }
}

impl ParsedValue for IDInput {
    type Value = Option<i32>;

    fn parsed_value(&self) -> &Self::Value {
        &self.id
    }
}

impl_draw_for_input!(IDInput);

pub struct PathInput {
    input: Input,
    base_path: PathBuf,
    path: Option<SeriesPath>,
}

impl PathInput {
    pub fn new(selected: bool, config: &Config) -> Self {
        Self {
            input: Input::new(selected),
            base_path: config.series_dir.clone(),
            path: None,
        }
    }
}

impl ValidatedInput for PathInput {
    fn label(&self) -> &'static str {
        "Path"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.path = None;
            self.input.error = false;
            return;
        }

        let path = SeriesPath::with_base(&self.base_path, Cow::Owned(PathBuf::from(text)));
        let exists = path.exists_base(&self.base_path);

        self.path = if exists { Some(path) } else { None };
        self.input.error = !exists;
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        "Path must exist".into()
    }
}

impl ParsedValue for PathInput {
    type Value = Option<SeriesPath>;

    fn parsed_value(&self) -> &Self::Value {
        &self.path
    }
}

impl_draw_for_input!(PathInput);

pub struct ParserInput {
    input: Input,
    parser: EpisodeParser,
}

impl ParserInput {
    pub fn new(selected: bool) -> Self {
        Self {
            input: Input::new(selected),
            parser: EpisodeParser::default(),
        }
    }
}

impl ValidatedInput for ParserInput {
    fn label(&self) -> &'static str {
        "Episode Regex"
    }

    fn input_mut(&mut self) -> &mut Input {
        &mut self.input
    }

    fn validate(&mut self) {
        let text = self.input.text();

        if text.is_empty() {
            self.parser = EpisodeParser::default();
            self.input.error = false;
            return;
        }

        let parser =
            EpisodeParser::custom_with_replacements(text, SERIES_TITLE_REP, SERIES_EPISODE_REP)
                .ok();

        self.input.error = parser.is_none();
        self.parser = parser.unwrap_or_else(Default::default);
    }

    fn has_error(&self) -> bool {
        self.input.error
    }

    fn error_message(&self) -> Cow<'static, str> {
        // TODO: use concat! macro if/when it can accept constants, or when a similiar crate doesn't require nightly
        format!(
            "Regex must contain \"{}\" and be valid",
            crate::SERIES_EPISODE_REP
        )
        .into()
    }
}

impl ParsedValue for ParserInput {
    type Value = EpisodeParser;

    fn parsed_value(&self) -> &Self::Value {
        &self.parser
    }
}

impl_draw_for_input!(ParserInput);
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use cgmath;
use cgmath::{vec3};
use cgmath::prelude::*;

type Point3 = cgmath::Point3<f32>;
type Vector3 = cgmath::Vector3<f32>;
type Matrix4 = cgmath::Matrix4<f32>;

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
pub enum Camera_Movement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
}

// Default camera values
const YAW: f32        = -90.0;
const PITCH: f32      =  0.0;
const SPEED: f32      =  2.5;
const SENSITIVTY: f32 =  0.1;
const ZOOM: f32       =  45.0;

struct Camera {
    // Camera Attributes
    pub Position: Vector3,
    pub Front: Vector3,
    pub Up: Vector3,
    pub Right: Vector3,
    pub WorldUp: Vector3,
    // Euler Angles
    pub Yaw: f32,
    pub Pitch: f32,
    // Camera options
    pub MovementSpeed: f32,
    pub MouseSensitivity: f32,
    pub Zoom: f32,
}

impl Camera {
    fn new(position: Vector3, up: Vector3, yaw: f32, pitch: f32) -> Camera {
        Camera {
            Position: position,
            WorldUp: up,
            Yaw: yaw,
            Pitch: pitch,
            ..Camera::default()
        }
    }

    // TODO? constructor with scalar values

    /// Calculates the front vector from the Camera's (updated) Eular Angles
    fn updateCameraVectors(&mut self) {
        // Calculate the new Front vector
        let front = Vector3 {
            x: self.Yaw.to_radians().cos() * self.Pitch.to_radians().cos(),
            y: self.Pitch.to_radians().sin(),
            z: self.Yaw.to_radians().sin() * self.Pitch.to_radians().cos()
        };
        self.Front = front.normalize();
        // Also re-calculate the Right and Up vector
        self.Right = self.Front.cross(self.WorldUp).normalize(); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        self.Up = self.Right.cross(self.Front).normalize();
    }
}

impl Default for Camera {
    fn default() -> Camera {
        let mut camera = Camera {
            Position: Vector3::zero(),
            Front: vec3(0.0, 0.0, -1.0),
            Up: Vector3::zero(),        // initialized later
            Right: Vector3::zero(),     // initialized later
            WorldUp: Vector3::unit_y(),
            Yaw: YAW,
            Pitch: PITCH,
            MovementSpeed: SPEED,
            MouseSensitivity: SENSITIVTY,
            Zoom: ZOOM,
        };
        camera.updateCameraVectors();
        camera
    }
}
<commit_msg>finish camera class<commit_after>#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use cgmath;
use cgmath::{vec3};
use cgmath::prelude::*;

type Point3 = cgmath::Point3<f32>;
type Vector3 = cgmath::Vector3<f32>;
type Matrix4 = cgmath::Matrix4<f32>;

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
#[derive(PartialEq)]
pub enum Camera_Movement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
}
use self::Camera_Movement::*;

// Default camera values
const YAW: f32        = -90.0;
const PITCH: f32      =  0.0;
const SPEED: f32      =  2.5;
const SENSITIVTY: f32 =  0.1;
const ZOOM: f32       =  45.0;

struct Camera {
    // Camera Attributes
    pub Position: Point3,
    pub Front: Vector3,
    pub Up: Vector3,
    pub Right: Vector3,
    pub WorldUp: Vector3,
    // Euler Angles
    pub Yaw: f32,
    pub Pitch: f32,
    // Camera options
    pub MovementSpeed: f32,
    pub MouseSensitivity: f32,
    pub Zoom: f32,
}

impl Camera {
    fn new(position: Point3, up: Vector3, yaw: f32, pitch: f32) -> Camera {
        Camera {
            Position: position,
            WorldUp: up,
            Yaw: yaw,
            Pitch: pitch,
            ..Camera::default()
        }
    }

    // TODO? constructor with scalar values

    /// Returns the view matrix calculated using Eular Angles and the LookAt Matrix
    pub fn GetViewMatrix(&self) -> Matrix4 {
        Matrix4::look_at(self.Position, self.Position + self.Front, self.Up)
    }

    /// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
    pub fn ProcessKeyboard(&mut self, direction: Camera_Movement, deltaTime: f32) {
        let velocity = self.MovementSpeed * deltaTime;
        if direction == FORWARD {
            self.Position += self.Front * velocity;
        }
        if direction == BACKWARD {
            self.Position += -(self.Front * velocity);
        }
        if direction == LEFT {
            self.Position += -(self.Right * velocity);
        }
        if direction == RIGHT {
            self.Position += self.Right * velocity;
        }
    }

    /// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
    pub fn ProcessMouseMovement(&mut self, mut xoffset: f32, mut yoffset: f32, constrainPitch: bool) {
        xoffset *= self.MouseSensitivity;
        yoffset *= self.MouseSensitivity;

        self.Yaw   += xoffset;
        self.Pitch += yoffset;

        // Make sure that when pitch is out of bounds, screen doesn't get flipped
        if constrainPitch {
            if self.Pitch > 89.0 {
                self.Pitch = 89.0;
            }
            if self.Pitch < -89.0 {
                self.Pitch = -89.0;
            }
        }

        // Update Front, Right and Up Vectors using the updated Eular angles
        self.updateCameraVectors();
    }

    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    pub fn ProcessMouseScroll(&mut self, yoffset: f32) {
        if self.Zoom >= 1.0 && self.Zoom <= 45.0 {
            self.Zoom -= yoffset;
        }
        if self.Zoom <= 1.0 {
            self.Zoom = 1.0;
        }
        if self.Zoom >= 45.0 {
            self.Zoom = 45.0;
        }
    }

    /// Calculates the front vector from the Camera's (updated) Eular Angles
    fn updateCameraVectors(&mut self) {
        // Calculate the new Front vector
        let front = Vector3 {
            x: self.Yaw.to_radians().cos() * self.Pitch.to_radians().cos(),
            y: self.Pitch.to_radians().sin(),
            z: self.Yaw.to_radians().sin() * self.Pitch.to_radians().cos()
        };
        self.Front = front.normalize();
        // Also re-calculate the Right and Up vector
        self.Right = self.Front.cross(self.WorldUp).normalize(); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        self.Up = self.Right.cross(self.Front).normalize();
    }
}

impl Default for Camera {
    fn default() -> Camera {
        let mut camera = Camera {
            Position: Point3::new(0.0, 0.0, 0.0),
            Front: vec3(0.0, 0.0, -1.0),
            Up: Vector3::zero(),        // initialized later
            Right: Vector3::zero(),     // initialized later
            WorldUp: Vector3::unit_y(),
            Yaw: YAW,
            Pitch: PITCH,
            MovementSpeed: SPEED,
            MouseSensitivity: SENSITIVTY,
            Zoom: ZOOM,
        };
        camera.updateCameraVectors();
        camera
    }
}
<|endoftext|>


<commit_before>
extern crate std;
extern crate core;
extern crate num;
extern crate uuid;

use std::str::SendStr;

use super::def::*;
use super::serialize::*;
use super::reader::*;
use std::collections::TreeMap;

pub static CQL_VERSION_STRINGS:  [&'static str, .. 3] = ["3.0.0", "3.0.0", "3.0.0"];
pub static CQL_MAX_SUPPORTED_VERSION:u8 = 0x03;

macro_rules! serialize_and_check_io_error(
    ($writer: expr, $obj: ident, $version: expr, $msg: expr) => {
        match $obj.serialize($writer, $version) {
            Err(e) => return Err(RCError { kind: SerializeError, desc: format!("{} -> {}", $msg, e.desc).into_maybe_owned()}),
            _ => ()
        }
    }
)

type PrepsStore = TreeMap<String, Box<CqlPreparedStat>>;

pub struct CqlClient {
    socket: std::io::net::tcp::TcpStream,
    pub version: u8,
    prepared: PrepsStore
}

impl CqlClient {

    fn new(socket: std::io::net::tcp::TcpStream, version: u8) -> CqlClient {
        CqlClient {socket: socket, version: version, prepared: TreeMap::new()}
    }

    fn build_auth<'a>(&self, creds: &'a Vec<SendStr>, stream: i8) -> CqlRequest<'a> {
        return CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: stream,
            opcode: OpcodeOptions,
            body: RequestCred(creds),
        };
    }

    fn build_options(&self) -> CqlRequest {
        return CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeOptions,
            body: RequestOptions,
        };
    }

    pub fn exec_query(&mut self, query_str: &str, con: Consistency::Consistency) -> RCResult<CqlResponse> {
        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeQuery,
            body: RequestQuery(query_str, con, 0)};

        let ref mut socket = self.socket;
        serialize_and_check_io_error!(socket, q, self.version, "Error serializing query");
        let res = read_and_check_io_error!(socket, read_cql_response, self.version, "Error reading query");
        Ok(res)
    }

    pub fn exec_prepared(&mut self, ps_id: &str, params: &[CqlValue], con: Consistency::Consistency) -> RCResult<CqlResponse> {
        let preps = match self.prepared.find(&String::from_str(ps_id)) {
            Some(ps) => ps,
            None => return Err(RCError::new(format!("Unknown prepared statement <{}>", ps_id), GenericError))
        };

        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeExecute,
            body: RequestExec(*preps, params, con, 0x01),
        };

        serialize_and_check_io_error!(&mut self.socket, q, self.version, "Error serializing prepared statement execution");

        /* Code to debug prepared statements. Write to file the serialization of the request

        let path = Path::new("prepared_data.bin");
        let display = path.display();
        let mut file = match std::io::File::create(&path) {
            Err(why) => fail!("couldn't create {}: {}", display, why.desc),
            Ok(file) => file,
        };

        serialize_and_check_io_error!(&mut file, q, self.version, "Error serializing query");

        */

        Ok(read_and_check_io_error!(self.socket, read_cql_response, self.version, "Error reading prepared statement execution result"))
    }

    pub fn prepared_statement(&mut self, query_str: &str, query_id: &str) -> RCResult<()> {
        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodePrepare,
            body: RequestPrepare(query_str),
        };

        serialize_and_check_io_error!(&mut self.socket, q, self.version, "Error serializing prepared statement");

        let res = read_and_check_io_error!(&mut self.socket, read_cql_response, self.version, "Error reading query");
        match res.body {
            ResultPrepared(preps) => {
                self.prepared.insert(String::from_str(query_id), preps);
                Ok(())
            },
            _ => Err(RCError::new("Response does not contain prepared statement", ReadError))
        }
    }
}

fn build_startup(version: u8) -> CqlRequest {
    let body = CqlStringMap {
        pairs:vec![CqlPair{key: "CQL_VERSION", value: CQL_VERSION_STRINGS[(version-1) as uint]}],
    };
    return CqlRequest {
        version: version,
        flags: 0x00,
        stream: 0x01,
        opcode: OpcodeStartup,
        body: RequestStartup(body),
    };
}

fn send_startup(socket: &mut std::io::TcpStream, version: u8, creds: Option<&Vec<SendStr>>) -> RCResult<()> {
        let msg_startup = build_startup(version);
        serialize_and_check_error!(socket, msg_startup, version, "Error serializing startup message");

        let response = read_and_check_io_error!(socket, read_cql_response, version, "Error reding response");
        match response.body {
            ResponseReady =>  Ok(()),
            ResponseAuth(_) => {
                match creds {
                    Some(cred) => {
                        let msg_auth = CqlRequest {
                            version: version,
                            flags: 0x00,
                            stream: 0x01,
                            opcode: OpcodeOptions,
                            body: RequestCred(cred),
                        };
                        serialize_and_check_error!(socket, msg_auth, version, "Error serializing request (auth)");

                        let response = read_and_check_io_error!(socket, read_cql_response, version, "Error reding authenticaton response");
                        match response.body {
                            ResponseReady => Ok(()),
                            ResponseError(_, ref msg) => Err(RCError::new(format!("Error in authentication: {}", msg), ReadError)),
                            _ => Err(RCError::new("Server returned unknown message", ReadError))
                        }
                    },
                    None => Err(RCError::new("Credential should be provided for authentication", ReadError))
                }
            },
            ResponseError(_, ref msg) => Err(RCError::new(format!("Error connecting: {}", msg), ReadError)),
            _ => Err(RCError::new("Wrong response to startup", ReadError))
        }
}

pub fn connect(ip: &'static str, port: u16, creds:Option<&Vec<SendStr>>) -> RCResult<CqlClient> {

    let mut version = CQL_MAX_SUPPORTED_VERSION;

    while version >= 0x01 {
        let res = std::io::TcpStream::connect(ip.as_slice(), port);
        if res.is_err() {
            return Err(RCError::new(format!("Failed to connect to server at {}:{}", ip.as_slice(), port), ConnectionError));
        }

        let mut socket = res.unwrap();

        match send_startup(& mut socket, version, creds) {
            Ok(_) => return Ok(CqlClient::new(socket, version)),
            Err(e) => println!("Error connecting with protocol version v{}: {}", version, e.desc)
        }
        version -= 1;
    }
    Err(RCError::new("Unable to find suitable protocol version (v1, v2, v3)", ReadError))
}
<commit_msg>Refactored send_startup()<commit_after>
extern crate std;
extern crate core;
extern crate num;
extern crate uuid;

use std::str::SendStr;

use super::def::*;
use super::serialize::*;
use super::reader::*;
use std::collections::TreeMap;

pub static CQL_VERSION_STRINGS:  [&'static str, .. 3] = ["3.0.0", "3.0.0", "3.0.0"];
pub static CQL_MAX_SUPPORTED_VERSION:u8 = 0x03;

macro_rules! serialize_and_check_io_error(
    ($writer: expr, $obj: ident, $version: expr, $msg: expr) => {
        match $obj.serialize($writer, $version) {
            Err(e) => return Err(RCError { kind: SerializeError, desc: format!("{} -> {}", $msg, e.desc).into_maybe_owned()}),
            _ => ()
        }
    }
)

type PrepsStore = TreeMap<String, Box<CqlPreparedStat>>;

pub struct CqlClient {
    socket: std::io::net::tcp::TcpStream,
    pub version: u8,
    prepared: PrepsStore
}

impl CqlClient {

    fn new(socket: std::io::net::tcp::TcpStream, version: u8) -> CqlClient {
        CqlClient {socket: socket, version: version, prepared: TreeMap::new()}
    }

    fn build_auth<'a>(&self, creds: &'a Vec<SendStr>, stream: i8) -> CqlRequest<'a> {
        return CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: stream,
            opcode: OpcodeOptions,
            body: RequestCred(creds),
        };
    }

    fn build_options(&self) -> CqlRequest {
        return CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeOptions,
            body: RequestOptions,
        };
    }

    pub fn exec_query(&mut self, query_str: &str, con: Consistency::Consistency) -> RCResult<CqlResponse> {
        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeQuery,
            body: RequestQuery(query_str, con, 0)};

        let ref mut socket = self.socket;
        serialize_and_check_io_error!(socket, q, self.version, "Error serializing query");
        let res = read_and_check_io_error!(socket, read_cql_response, self.version, "Error reading query");
        Ok(res)
    }

    pub fn exec_prepared(&mut self, ps_id: &str, params: &[CqlValue], con: Consistency::Consistency) -> RCResult<CqlResponse> {
        let preps = match self.prepared.find(&String::from_str(ps_id)) {
            Some(ps) => &**ps,
            None => return Err(RCError::new(format!("Unknown prepared statement <{}>", ps_id), GenericError))
        };

        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodeExecute,
            body: RequestExec(preps, params, con, 0x01),
        };

        serialize_and_check_io_error!(&mut self.socket, q, self.version, "Error serializing prepared statement execution");

        /* Code to debug prepared statements. Write to file the serialization of the request

        let path = Path::new("prepared_data.bin");
        let display = path.display();
        let mut file = match std::io::File::create(&path) {
            Err(why) => fail!("couldn't create {}: {}", display, why.desc),
            Ok(file) => file,
        };

        serialize_and_check_io_error!(&mut file, q, self.version, "Error serializing query");

        */

        Ok(read_and_check_io_error!(self.socket, read_cql_response, self.version, "Error reading prepared statement execution result"))
    }

    pub fn prepared_statement(&mut self, query_str: &str, query_id: &str) -> RCResult<()> {
        let q = CqlRequest {
            version: self.version,
            flags: 0x00,
            stream: 0x01,
            opcode: OpcodePrepare,
            body: RequestPrepare(query_str),
        };

        serialize_and_check_io_error!(&mut self.socket, q, self.version, "Error serializing prepared statement");

        let res = read_and_check_io_error!(&mut self.socket, read_cql_response, self.version, "Error reading query");
        match res.body {
            ResultPrepared(preps) => {
                self.prepared.insert(String::from_str(query_id), preps);
                Ok(())
            },
            _ => Err(RCError::new("Response does not contain prepared statement", ReadError))
        }
    }
}

fn send_startup(socket: &mut std::io::TcpStream, version: u8, creds: Option<&Vec<SendStr>>) -> RCResult<()> {
    let body = CqlStringMap {
        pairs:vec![CqlPair{key: "CQL_VERSION", value: CQL_VERSION_STRINGS[(version-1) as uint]}],
    };
    let msg_startup = CqlRequest {
        version: version,
        flags: 0x00,
        stream: 0x01,
        opcode: OpcodeStartup,
        body: RequestStartup(body),
    };
    serialize_and_check_error!(socket, msg_startup, version, "Error serializing startup message");

    let response = read_and_check_io_error!(socket, read_cql_response, version, "Error reding response");
    match response.body {
        ResponseReady =>  Ok(()),
        ResponseAuth(_) => {
            match creds {
                Some(cred) => {
                    let msg_auth = CqlRequest {
                        version: version,
                        flags: 0x00,
                        stream: 0x01,
                        opcode: OpcodeOptions,
                        body: RequestCred(cred),
                    };
                    serialize_and_check_error!(socket, msg_auth, version, "Error serializing request (auth)");

                    let response = read_and_check_io_error!(socket, read_cql_response, version, "Error reding authenticaton response");
                    match response.body {
                        ResponseReady => Ok(()),
                        ResponseError(_, ref msg) => Err(RCError::new(format!("Error in authentication: {}", msg), ReadError)),
                        _ => Err(RCError::new("Server returned unknown message", ReadError))
                    }
                },
                None => Err(RCError::new("Credential should be provided for authentication", ReadError))
            }
        },
        ResponseError(_, ref msg) => Err(RCError::new(format!("Error connecting: {}", msg), ReadError)),
        _ => Err(RCError::new("Wrong response to startup", ReadError))
    }
}

pub fn connect(ip: &'static str, port: u16, creds:Option<&Vec<SendStr>>) -> RCResult<CqlClient> {

    let mut version = CQL_MAX_SUPPORTED_VERSION;

    while version >= 0x01 {
        let res = std::io::TcpStream::connect(ip.as_slice(), port);
        if res.is_err() {
            return Err(RCError::new(format!("Failed to connect to server at {}:{}", ip.as_slice(), port), ConnectionError));
        }

        let mut socket = res.unwrap();

        match send_startup(& mut socket, version, creds) {
            Ok(_) => return Ok(CqlClient::new(socket, version)),
            Err(e) => println!("Error connecting with protocol version v{}: {}", version, e.desc)
        }
        version -= 1;
    }
    Err(RCError::new("Unable to find suitable protocol version (v1, v2, v3)", ReadError))
}
<|endoftext|>


<commit_before>use std::io;
use std::io::net::tcp;
use std::io::BufferedReader;
use std::io::util::LimitReader;
use common::{Header, HTTPVersion, Method};
use Request;
use url::Path;

/// A ClientConnection is an object that will store a socket to a client
/// and return Request objects.
pub struct ClientConnection {
    initial_socket: tcp::TcpStream,         // copy of the socket to be passed to request objects
	socket: BufferedReader<tcp::TcpStream>,
}

impl ClientConnection {
    pub fn new(socket: tcp::TcpStream) -> ClientConnection {
        ClientConnection {
            initial_socket: socket.clone(),
            socket: BufferedReader::new(socket),
        }
    }

    /// Generates an IoError with invalid input.
    /// This function is here because it is incredibly annoying to create this error.
    fn gen_invalid_input(desc: &'static str) -> io::IoError {
        io::IoError {
            kind: io::InvalidInput,
            desc: desc,
            detail: None
        }
    }

    /// Parses a "HTTP/1.1" string.
    fn parse_http_version(version: &str) -> io::IoResult<HTTPVersion> {
        let elems = version.splitn('/', 2).map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        let elems = elems.get(1).as_slice().splitn('.', 2)
            .map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        match (from_str(elems.get(0).as_slice()), from_str(elems.get(1).as_slice())) {
            (Some(major), Some(minor)) =>
                Ok(HTTPVersion(major, minor)),
            _ => Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }
    }

    /// Parses the request line of the request.
    /// eg. GET / HTTP/1.1
    fn parse_request_line(line: &str) -> io::IoResult<(Method, Path, HTTPVersion)> {
        let mut words = line.words();

        let method = words.next();
        let path = words.next();
        let version = words.next();

        let (method, path, version) = match (method, path, version) {
            (Some(m), Some(p), Some(v)) => (m, p, v),
            _ => return Err(ClientConnection::gen_invalid_input("Missing element in request line"))
        };

        let method = match from_str(method) {
            Some(method) => method,
            None => return Err(ClientConnection::gen_invalid_input("Could not parse method"))
        };

        let path = match Path::parse(path) {
            Ok(p) => p,
            Err(_) => return Err(ClientConnection::gen_invalid_input("Wrong requested URL"))
        };

        let version = try!(ClientConnection::parse_http_version(version));

        Ok((method, path, version))
    }

    /// Parses a header line.
    /// eg. Host: example.com
    fn parse_header(line: &str) -> io::IoResult<Header> {
        let elems = line.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (no ':')"))
        }
        if elems.get(1).as_slice().chars().next() != Some(' ') {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (missing space after ':')"))
        }

        let field = match from_str(elems.get(0).as_slice()) {
            None => return Err(ClientConnection::gen_invalid_input("Could not parse header")),
            Some(f) => f
        };

        Ok(Header {
            field: field,
            value: elems.get(1).as_slice().slice_from(1).to_string()
        })
    }

    /// Reads a request from the stream.
    /// Blocks until the header has been read.
    fn read(&mut self) -> io::IoResult<Request> {
        let mut lines = self.socket.lines();

        // reading the request line
        let (method, path, version) =
            try!(ClientConnection::parse_request_line(
                match lines.next() {
                    Some(line) => try!(line),
                    None => return Err(ClientConnection::gen_invalid_input(
                                "Missing request line"))
                }.as_slice().trim()
            ));

        // getting all headers
        let headers = {
            let mut headers = Vec::new();
            loop {
                match lines.next() {
                    Some(line) => {
                        let line = try!(line);
                        if line.as_slice().trim().len() == 0 { break };
                        headers.push(
                            try!(ClientConnection::parse_header(line.as_slice().trim()))
                        )
                    },
                    None => break
                }
            }
            headers
        };

        // finding length of body
        let body_length = headers.iter()
            .find(|h| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
            .unwrap_or(0u);

        // building the request
        Ok(Request {
            read_socket: LimitReader::new(
                        BufferedReader::new(self.initial_socket.clone()), body_length
                    ),
            write_socket: self.initial_socket.clone(),
            method: method,
            path: path,
            http_version: version,
            headers: headers,
            body_length: body_length,
        })
    }
}

impl Iterator<Request> for ClientConnection {
    /// Blocks until the next Request is available.
    /// Returns None when the connection to the client has been closed.
    fn next(&mut self) -> Option<Request> {
        // TODO: send back message to client
        loop {
            return match self.read() {
                Err(_) => None,
                Ok(rq) => Some(rq)
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::ClientConnection;

    #[test]
    fn test_parse_request_line() {
        let (method, path, ver) =
            ClientConnection::parse_request_line("GET /hello HTTP/1.1").unwrap();

        assert!(method.equiv(&"get"));
        assert!(path == from_str("/hello").unwrap());
        assert!(ver == ::common::HTTPVersion(1, 1));

        assert!(ClientConnection::parse_request_line("GET /hello").is_err());
        assert!(ClientConnection::parse_request_line("qsd qsd qsd").is_err());
    }

    #[test]
    fn test_parse_header() {
        let header = ClientConnection::parse_header("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(ClientConnection::parse_header("hello world").is_err());
    }
}
<commit_msg>Handling Connection: close header<commit_after>use std::io;
use std::io::net::tcp;
use std::io::BufferedReader;
use std::io::util::LimitReader;
use common::{Header, HTTPVersion, Method};
use Request;
use url::Path;

/// A ClientConnection is an object that will store a socket to a client
/// and return Request objects.
pub struct ClientConnection {
    // copy of the socket to be passed to request objects
    initial_socket: tcp::TcpStream,

    //
	socket: BufferedReader<tcp::TcpStream>,

    // set to true if the client sent a "Connection: close" in the previous request
    connection_must_close: bool,
}

impl ClientConnection {
    pub fn new(socket: tcp::TcpStream) -> ClientConnection {
        ClientConnection {
            initial_socket: socket.clone(),
            socket: BufferedReader::new(socket),
            connection_must_close: false,
        }
    }

    /// Generates an IoError with invalid input.
    /// This function is here because it is incredibly annoying to create this error.
    fn gen_invalid_input(desc: &'static str) -> io::IoError {
        io::IoError {
            kind: io::InvalidInput,
            desc: desc,
            detail: None
        }
    }

    /// Parses a "HTTP/1.1" string.
    fn parse_http_version(version: &str) -> io::IoResult<HTTPVersion> {
        let elems = version.splitn('/', 2).map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        let elems = elems.get(1).as_slice().splitn('.', 2)
            .map(|e| e.to_string()).collect::<Vec<String>>();
        if elems.len() != 2 {
            return Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }

        match (from_str(elems.get(0).as_slice()), from_str(elems.get(1).as_slice())) {
            (Some(major), Some(minor)) =>
                Ok(HTTPVersion(major, minor)),
            _ => Err(ClientConnection::gen_invalid_input("Wrong HTTP version format"))
        }
    }

    /// Parses the request line of the request.
    /// eg. GET / HTTP/1.1
    fn parse_request_line(line: &str) -> io::IoResult<(Method, Path, HTTPVersion)> {
        let mut words = line.words();

        let method = words.next();
        let path = words.next();
        let version = words.next();

        let (method, path, version) = match (method, path, version) {
            (Some(m), Some(p), Some(v)) => (m, p, v),
            _ => return Err(ClientConnection::gen_invalid_input("Missing element in request line"))
        };

        let method = match from_str(method) {
            Some(method) => method,
            None => return Err(ClientConnection::gen_invalid_input("Could not parse method"))
        };

        let path = match Path::parse(path) {
            Ok(p) => p,
            Err(_) => return Err(ClientConnection::gen_invalid_input("Wrong requested URL"))
        };

        let version = try!(ClientConnection::parse_http_version(version));

        Ok((method, path, version))
    }

    /// Parses a header line.
    /// eg. Host: example.com
    fn parse_header(line: &str) -> io::IoResult<Header> {
        let elems = line.splitn(':', 2).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (no ':')"))
        }
        if elems.get(1).as_slice().chars().next() != Some(' ') {
            return Err(ClientConnection::gen_invalid_input(
                "Wrong header format (missing space after ':')"))
        }

        let field = match from_str(elems.get(0).as_slice()) {
            None => return Err(ClientConnection::gen_invalid_input("Could not parse header")),
            Some(f) => f
        };

        Ok(Header {
            field: field,
            value: elems.get(1).as_slice().slice_from(1).to_string()
        })
    }

    /// Reads a request from the stream.
    /// Blocks until the header has been read.
    fn read(&mut self) -> io::IoResult<Request> {
        let mut lines = self.socket.lines();

        // reading the request line
        let (method, path, version) =
            try!(ClientConnection::parse_request_line(
                match lines.next() {
                    Some(line) => try!(line),
                    None => return Err(ClientConnection::gen_invalid_input(
                                "Missing request line"))
                }.as_slice().trim()
            ));

        // getting all headers
        let headers = {
            let mut headers = Vec::new();
            loop {
                match lines.next() {
                    Some(line) => {
                        let line = try!(line);
                        if line.as_slice().trim().len() == 0 { break };
                        headers.push(
                            try!(ClientConnection::parse_header(line.as_slice().trim()))
                        )
                    },
                    None => break
                }
            }
            headers
        };

        // finding length of body
        let body_length = headers.iter()
            .find(|h| h.field.equiv(&"Content-Length"))
            .and_then(|h| from_str::<uint>(h.value.as_slice()))
            .unwrap_or(0u);

        // building the request
        Ok(Request {
            read_socket: LimitReader::new(
                        BufferedReader::new(self.initial_socket.clone()), body_length
                    ),
            write_socket: self.initial_socket.clone(),
            method: method,
            path: path,
            http_version: version,
            headers: headers,
            body_length: body_length,
        })
    }
}

impl Iterator<Request> for ClientConnection {
    /// Blocks until the next Request is available.
    /// Returns None when no new Requests will come from the client.
    fn next(&mut self) -> Option<Request> {
        // the client sent a "connection: close" header in this previous request
        //  or is using HTTP 1.0, meaning that no new request will come
        if self.connection_must_close {
            return None
        }

        // TODO: send back message to client in case of parsing error
        loop {
            let rq = match self.read() {
                Err(_) => return None,
                Ok(rq) => rq
            };

            // updating the status of the connection
            {
                let connection_header = rq.headers.iter()
                    .find(|h| h.field.equiv(&"Connection")).map(|h| h.value.as_slice());

                if connection_header == Some("close") {
                    self.connection_must_close = true;
                } else if rq.http_version == HTTPVersion(1, 0) &&
                        connection_header != Some("keep-alive")
                {
                    self.connection_must_close = true;
                }
            }

            // returning the request
            return Some(rq);
        }
    }
}

#[cfg(test)]
mod test {
    use super::ClientConnection;

    #[test]
    fn test_parse_request_line() {
        let (method, path, ver) =
            ClientConnection::parse_request_line("GET /hello HTTP/1.1").unwrap();

        assert!(method.equiv(&"get"));
        assert!(path == from_str("/hello").unwrap());
        assert!(ver == ::common::HTTPVersion(1, 1));

        assert!(ClientConnection::parse_request_line("GET /hello").is_err());
        assert!(ClientConnection::parse_request_line("qsd qsd qsd").is_err());
    }

    #[test]
    fn test_parse_header() {
        let header = ClientConnection::parse_header("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(ClientConnection::parse_header("hello world").is_err());
    }
}
<|endoftext|>


<commit_before>use error;
use reqwest;
use reqwest::header;
use serde_json;
use serde_urlencoded;
use std::env;
use std::fmt;
use std::io::Read;
use Endpoint;
use PageableEndpoint;
use Pager;
use std::time;
use std::thread;
use std::sync::Arc;
use std::sync::Mutex;
use std;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

#[derive(Debug, Clone)]
pub struct ClientConfig {
	pub cooldown: time::Duration,
}

impl Default for ClientConfig {

	/// Default ClientConfig:
	/// - No cooldown

	fn default() -> Self {
		Self {
			cooldown: time::Duration::new(0, 0),
		}
	}
}

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
	config: ClientConfig,
	last_request: Arc<Mutex<Option<time::Instant>>>,
}

#[allow(unused)]
impl Client {
	/// Create a new api client with a given api key
	pub fn new(api_key: String) -> Self
	{
		let crates_name = env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

		let crates_version = &env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
			config: ClientConfig::default(),
			last_request: Arc::new(Mutex::new(None))
		}
	}

	/// load api key from an environment variable
	pub fn with_env_key(env_key: &str) -> Option<Client>
	{
		match env::var_os(env_key)
		{
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	pub fn cooldown_time(&mut self, time: time::Duration) {
		self.config.cooldown = time;
	}

	/// execute a request that satisfies [`Endpoint`](../trait.Endpoint.html)
	pub fn execute<T>(&mut self, mut endpoint: T) -> Result<T::ResponseType, error::Error>
	where
		T: super::Endpoint + Clone + fmt::Debug
	{

		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(reqwest::Method::Post, uristring.parse().unwrap());

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.clone())
		{
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		debug!("creating reqwest client");
		let client = reqwest::Client::new();

		self.waiting_for_cooldown();
		debug!("sending request");

		let response = client.execute(http_req);
		debug!("response received");
		self.reset_cooldown();

		// This section needs some rewriting. maybe... later
		match response
		{
			Err(e) => return Err(error::Error::Http),
			Ok(mut res) => {
				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				debug!("Answer: {}", json_string);

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(&json_string)
				{
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(error::Error::Api(error::api::Api::new(r.code.unwrap(), r.message)))
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}

	fn remaining_cooldown_time(&self) -> time::Duration {
		match *self.last_request.lock().unwrap() {
			None => self.config.cooldown,
			Some(time) => {
				time.elapsed().clone()
			}
		}
	}

	fn reset_cooldown(&mut self) {
		*self.last_request.lock().unwrap() = Some(time::Instant::now());
	}

	// vlocking till we can create a new request
	fn waiting_for_cooldown(&mut self) {
		let conf_cooldown = self.config.cooldown;
		let since_last = self.remaining_cooldown_time();

		debug!("configurated cooldown {:2}.{}s", conf_cooldown.as_secs(), conf_cooldown.subsec_nanos());
		debug!("last request {:2}.{}s ago", since_last.as_secs(), since_last.subsec_nanos());

		if since_last >= conf_cooldown {
			return
		}

		let wait = self.config.cooldown - self.remaining_cooldown_time();
		debug!("waiting for cooldown: {:2}.{}s", wait.as_secs(), wait.subsec_nanos());

		thread::sleep(wait);
	}

	pub fn pager<T>(self, mut endpoint: T) -> Pager<T>
	where
		T: Endpoint + PageableEndpoint + Clone + fmt::Debug,
		<T as Endpoint>::ResponseType: IntoIterator + Clone + fmt::Debug,
		<<T as Endpoint>::ResponseType as std::iter::IntoIterator>::Item: Clone + fmt::Debug,

	{
		Pager::new(self, endpoint, None, None)
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<commit_msg>improve logging<commit_after>use error;
use reqwest;
use reqwest::header;
use serde_json;
use serde_urlencoded;
use std::env;
use std::fmt;
use std::io::Read;
use Endpoint;
use PageableEndpoint;
use Pager;
use std::time;
use std::thread;
use std::sync::Arc;
use std::sync::Mutex;
use std;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

#[derive(Debug, Clone)]
pub struct ClientConfig {
	pub cooldown: time::Duration,
}

impl Default for ClientConfig {

	/// Default ClientConfig:
	/// - No cooldown

	fn default() -> Self {
		Self {
			cooldown: time::Duration::new(0, 0),
		}
	}
}

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
	config: ClientConfig,
	last_request: Arc<Mutex<Option<time::Instant>>>,
}

#[allow(unused)]
impl Client {
	/// Create a new api client with a given api key
	pub fn new(api_key: String) -> Self
	{
		let crates_name = env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

		let crates_version = &env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
			config: ClientConfig::default(),
			last_request: Arc::new(Mutex::new(None))
		}
	}

	/// load api key from an environment variable
	pub fn with_env_key(env_key: &str) -> Option<Client>
	{
		match env::var_os(env_key)
		{
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	pub fn cooldown_time(&mut self, time: time::Duration) {
		self.config.cooldown = time;
	}

	/// execute a request that satisfies [`Endpoint`](../trait.Endpoint.html)
	pub fn execute<T>(&mut self, mut endpoint: T) -> Result<T::ResponseType, error::Error>
	where
		T: super::Endpoint + Clone + fmt::Debug
	{

		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(reqwest::Method::Post, uristring.parse().unwrap());

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.clone())
		{
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		debug!("creating reqwest client");
		let client = reqwest::Client::new();

		self.waiting_for_cooldown();
		debug!("sending request");

		let response = client.execute(http_req);
		debug!("response received");
		self.reset_cooldown();

		// This section needs some rewriting. maybe... later
		match response
		{
			Err(e) => return Err(error::Error::Http),
			Ok(mut res) => {
				debug!("http response code: {}", res.status());

				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				debug!("http response length: {}b", json_string.len());

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(&json_string)
				{
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(error::Error::Api(error::api::Api::new(r.code.unwrap(), r.message)))
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}

	fn remaining_cooldown_time(&self) -> time::Duration {
		match *self.last_request.lock().unwrap() {
			None => self.config.cooldown,
			Some(time) => {
				time.elapsed().clone()
			}
		}
	}

	fn reset_cooldown(&mut self) {
		*self.last_request.lock().unwrap() = Some(time::Instant::now());
	}

	// vlocking till we can create a new request
	fn waiting_for_cooldown(&mut self) {
		let conf_cooldown = self.config.cooldown;
		let since_last = self.remaining_cooldown_time();

		debug!("configurated cooldown {:2}.{}s", conf_cooldown.as_secs(), conf_cooldown.subsec_nanos());
		debug!("last request {:2}.{}s ago", since_last.as_secs(), since_last.subsec_nanos());

		if since_last >= conf_cooldown {
			return
		}

		let wait = self.config.cooldown - self.remaining_cooldown_time();
		debug!("waiting for cooldown: {:2}.{}s", wait.as_secs(), wait.subsec_nanos());

		thread::sleep(wait);
	}

	pub fn pager<T>(self, mut endpoint: T) -> Pager<T>
	where
		T: Endpoint + PageableEndpoint + Clone + fmt::Debug,
		<T as Endpoint>::ResponseType: IntoIterator + Clone + fmt::Debug,
		<<T as Endpoint>::ResponseType as std::iter::IntoIterator>::Item: Clone + fmt::Debug,

	{
		Pager::new(self, endpoint, None, None)
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<|endoftext|>


<commit_before>use log::LevelFilter;

pub use chrono::offset::{FixedOffset, Local, Offset, TimeZone, Utc};
use std::borrow::Cow;

/// Configuration for the Loggers
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Pass this struct to your logger to change when these information shall
/// be logged.
///
/// Construct using `Default` or using `ConfigBuilder`
#[derive(Debug, Clone)]
pub struct Config {
    pub(crate) time: LevelFilter,
    pub(crate) level: LevelFilter,
    pub(crate) thread: LevelFilter,
    pub(crate) target: LevelFilter,
    pub(crate) location: LevelFilter,
    pub(crate) time_format: Cow<'static, str>,
    pub(crate) time_offset: FixedOffset,
    pub(crate) filter_allow: Cow<'static, [Cow<'static, str>]>,
    pub(crate) filter_ignore: Cow<'static, [Cow<'static, str>]>,
}

/// Builder for the Logger Confogurations (`Config`)
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Use this struct to create a custom `Config` changing when these information shall
/// be logged. Every part can be enabled for a specific Level and is then
/// automatically enable for all lower levels as well.
///
/// The Result is that the logging gets more detailed the more verbose it gets.
/// E.g. to have one part shown always use `Level::Error`. But if you
/// want to show the source line only on `Trace` use that.
#[derive(Debug, Clone)]
pub struct ConfigBuilder(Config);

impl ConfigBuilder {
    /// Create a new default ConfigBuilder
    pub fn new() -> ConfigBuilder {
        ConfigBuilder(Config::default())
    }

    /// Set at which level and below the level itself shall be logged (default is Error)
    pub fn set_max_level<'a>(&'a mut self, level: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.level = level;
        self
    }

    /// Set at which level and below the current time shall be logged (default is Error)
    pub fn set_time_level<'a>(&'a mut self, time: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.time = time;
        self
    }

    /// Set at which level and below the thread id shall be logged. (default is Debug)
    pub fn set_thread_level<'a>(&'a mut self, thread: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.thread = thread;
        self
    }

    /// Set at which level and below the target shall be logged. (default is Debug)
    pub fn set_target_level<'a>(&'a mut self, target: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.target = target;
        self
    }

    /// Set at which level and below a source code reference shall be logged (default is Trace)
    pub fn set_location_level<'a>(&'a mut self, location: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.location = location;
        self
    }

    /// Set time chrono strftime format string. See: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Borrowed(time_format);
        self
    }

    /// Set time chrono strftime format string. See: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Owned(time_format);
        self
    }

    /// Set offset used for logging time (default is 0)
    pub fn set_time_offset<'a>(&'a mut self, time_offset: FixedOffset) -> &'a mut ConfigBuilder {
        self.0.time_offset = time_offset;
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow_str("tokio::uds")` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow(format!("{}{}","tokio", "uds"))` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Owned(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Clear allowed module filters.
    /// If none are specified, nothing is filtered out
    pub fn clear_filter_allow<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_allow = Cow::Borrowed(&[]);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore_str("tokio::uds")` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore(format!("{}{}","tokio", "uds"))` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Owned(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Clear ignore module filters.
    /// If none are specified, nothing is filtered
    pub fn clear_filter_ignore<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_ignore = Cow::Borrowed(&[]);
        self
    }

    /// Build new `Config`
    pub fn build(&mut self) -> Config {
        self.0.clone()
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            time: LevelFilter::Error,
            level: LevelFilter::Error,
            thread: LevelFilter::Debug,
            target: LevelFilter::Debug,
            location: LevelFilter::Trace,
            time_format: Cow::Borrowed("%H:%M:%S"),
            time_offset: FixedOffset::east(0),
            filter_allow: Cow::Borrowed(&[]),
            filter_ignore: Cow::Borrowed(&[]),
        }
    }
}
<commit_msg>Add local timezone based logging<commit_after>use log::LevelFilter;

pub use chrono::offset::{FixedOffset, Local, Offset, TimeZone, Utc};
use std::borrow::Cow;

/// Configuration for the Loggers
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Pass this struct to your logger to change when these information shall
/// be logged.
///
/// Construct using `Default` or using `ConfigBuilder`
#[derive(Debug, Clone)]
pub struct Config {
    pub(crate) time: LevelFilter,
    pub(crate) level: LevelFilter,
    pub(crate) thread: LevelFilter,
    pub(crate) target: LevelFilter,
    pub(crate) location: LevelFilter,
    pub(crate) time_format: Cow<'static, str>,
    pub(crate) time_offset: FixedOffset,
    pub(crate) time_local: bool,
    pub(crate) filter_allow: Cow<'static, [Cow<'static, str>]>,
    pub(crate) filter_ignore: Cow<'static, [Cow<'static, str>]>,
}

/// Builder for the Logger Confogurations (`Config`)
///
/// All loggers print the message in the following form:
/// `00:00:00 [LEVEL] crate::module: [lib.rs::100] your_message`
/// Every space delimited part except the actual message is optional.
///
/// Use this struct to create a custom `Config` changing when these information shall
/// be logged. Every part can be enabled for a specific Level and is then
/// automatically enable for all lower levels as well.
///
/// The Result is that the logging gets more detailed the more verbose it gets.
/// E.g. to have one part shown always use `Level::Error`. But if you
/// want to show the source line only on `Trace` use that.
#[derive(Debug, Clone)]
pub struct ConfigBuilder(Config);

impl ConfigBuilder {
    /// Create a new default ConfigBuilder
    pub fn new() -> ConfigBuilder {
        ConfigBuilder(Config::default())
    }

    /// Set at which level and below the level itself shall be logged (default is Error)
    pub fn set_max_level<'a>(&'a mut self, level: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.level = level;
        self
    }

    /// Set at which level and below the current time shall be logged (default is Error)
    pub fn set_time_level<'a>(&'a mut self, time: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.time = time;
        self
    }

    /// Set at which level and below the thread id shall be logged. (default is Debug)
    pub fn set_thread_level<'a>(&'a mut self, thread: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.thread = thread;
        self
    }

    /// Set at which level and below the target shall be logged. (default is Debug)
    pub fn set_target_level<'a>(&'a mut self, target: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.target = target;
        self
    }

    /// Set at which level and below a source code reference shall be logged (default is Trace)
    pub fn set_location_level<'a>(&'a mut self, location: LevelFilter) -> &'a mut ConfigBuilder {
        self.0.location = location;
        self
    }

    /// Set time chrono strftime format string. See: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Borrowed(time_format);
        self
    }

    /// Set time chrono strftime format string. See: https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers
    pub fn set_time_format<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        self.0.time_format = Cow::Owned(time_format);
        self
    }

    /// Set offset used for logging time (default is 0)
    pub fn set_time_offset<'a>(&'a mut self, time_offset: FixedOffset) -> &'a mut ConfigBuilder {
        self.0.time_offset = time_offset;
        self
    }

    /// set if you log in local timezone or UTC (default is UTC)
    pub fn set_time_to_local<'a>(&'a mut self, local: bool) -> &'a mut ConfigBuilder {
        self.0.time_local = local;
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow_str("tokio::uds")` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Add allowed module filters.
    /// If any are specified, only records from modules starting with one of these entries will be printed
    ///
    /// For example, `add_filter_allow(format!("{}{}","tokio", "uds"))` would allow only logging from the `tokio` crates `uds` module.
    pub fn add_filter_allow<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_allow);
        list.push(Cow::Owned(time_format));
        self.0.filter_allow = Cow::Owned(list);
        self
    }

    /// Clear allowed module filters.
    /// If none are specified, nothing is filtered out
    pub fn clear_filter_allow<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_allow = Cow::Borrowed(&[]);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore_str("tokio::uds")` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore_str<'a>(&'a mut self, time_format: &'static str) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Borrowed(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Add denied module filters.
    /// If any are specified, records from modules starting with one of these entries will be ignored
    ///
    /// For example, `add_filter_ignore(format!("{}{}","tokio", "uds"))` would deny logging from the `tokio` crates `uds` module.
    pub fn add_filter_ignore<'a>(&'a mut self, time_format: String) -> &'a mut ConfigBuilder {
        let mut list = Vec::from(&*self.0.filter_ignore);
        list.push(Cow::Owned(time_format));
        self.0.filter_ignore = Cow::Owned(list);
        self
    }

    /// Clear ignore module filters.
    /// If none are specified, nothing is filtered
    pub fn clear_filter_ignore<'a>(&'a mut self) -> &'a mut ConfigBuilder {
        self.0.filter_ignore = Cow::Borrowed(&[]);
        self
    }

    /// Build new `Config`
    pub fn build(&mut self) -> Config {
        self.0.clone()
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            time: LevelFilter::Error,
            level: LevelFilter::Error,
            thread: LevelFilter::Debug,
            target: LevelFilter::Debug,
            location: LevelFilter::Trace,
            time_format: Cow::Borrowed("%H:%M:%S"),
            time_offset: FixedOffset::east(0),
            time_local: false,
            filter_allow: Cow::Borrowed(&[]),
            filter_ignore: Cow::Borrowed(&[]),
        }
    }
}
<|endoftext|>