<commit_before>use config::Config;
use layout::{Layout, LayoutBox};
use xlib::Window;
use xlib_window_system::XlibWindowSystem;
use self::MoveOp::*;
use std::cmp;

pub struct WorkspaceConfig {
  pub tag: String,
  pub screen: uint,
  pub layout: LayoutBox
}

pub struct Workspace {
  managed: Vec<Window>,
  unmanaged: Vec<Window>,
  focused_window: Window,
  tag: String,
  screen: uint,
  visible: bool,
  layout: Box<Layout + 'static>
}

pub enum MoveOp {
  Up,
  Down,
  Swap
}

impl Workspace {
  pub fn add_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if !ws.is_window_floating(window) {
      self.managed.push(window);

      if self.unmanaged.len() > 0 {
        ws.restack_windows(self.unmanaged.iter_mut().chain(self.managed.iter_mut()).map(|&x| x).collect());
      }
    } else {
      self.unmanaged.push(window);
    }

    self.focus_window(ws, config, window);
    if self.visible {
      self.redraw(ws, config);
      ws.map_window(window);
    }
  }

  fn is_managed(&self, window: Window) -> bool {
    self.managed.iter().any(|&x| x == window)
  }

  fn is_unmanaged(&self, window: Window) -> bool {
    self.unmanaged.iter().any(|&x| x == window)
  }

  fn get_managed(&self, window: Window) -> uint {
    self.managed.iter().enumerate().filter(|&(_,&w)| w == window).map(|(i,_)| i).last().unwrap()
  }

  fn get_unmanaged(&self, window: Window) -> uint {
    self.unmanaged.iter().enumerate().filter(|&(_,&w)| w == window).map(|(i,_)| i).last().unwrap()
  }

  pub fn get_layout(&self) -> &Box<Layout + 'static> {
    &self.layout
  }

  pub fn get_tag(&self) -> String {
    self.tag.clone()
  }

  fn remove_managed(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    let index = self.get_managed(window);

    self.focused_window = 0;
    ws.unmap_window(window);
    self.managed.remove(index);

    let new_focused_window = if !self.managed.is_empty() {
      self.managed[if index < self.managed.len() { index } else { index - 1}]
    } else {
      0
    };

    if self.visible {
      self.redraw(ws, config);
      self.focus_window(ws, config, new_focused_window);
    }
  }

  fn remove_unmanaged(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    let index = self.get_unmanaged(window);

    self.focused_window = 0;
    ws.unmap_window(window);
    self.unmanaged.remove(index);

    let new_focused_window = if !self.unmanaged.is_empty() {
      self.unmanaged[if index < self.unmanaged.len() { index } else { index - 1}]
    } else {
      0
    };

    if self.visible {
      self.redraw(ws, config);
      self.focus_window(ws, config, new_focused_window);
    }
  }

  pub fn remove_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if self.is_managed(window) {
      self.remove_managed(ws, config, window);
    } else {
      if self.is_unmanaged(window) {
        self.remove_unmanaged(ws, config, window);
      }
    }
  }

  pub fn focus_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if window == 0 {
      return;
    }

    self.unfocus_window(ws, config);
    self.focused_window = window;
    ws.focus_window(window, config.border_focus_color);
  }

  pub fn unfocus_window(&mut self, ws: &XlibWindowSystem, config: &Config) {
    if self.focused_window != 0 {
      ws.set_window_border_color(self.focused_window, config.border_color);
      self.focused_window = 0;
    }
  }

  pub fn move_focus(&mut self, ws: &XlibWindowSystem, config: &Config, op: MoveOp) {
    let windows : Vec<Window> = self.managed.iter().chain(self.unmanaged.iter()).map(|&x| x).collect();
    let count = windows.len();

    if self.focused_window == 0 || count < 2 {
      return;
    }

    let index = windows.iter().enumerate().find(|&(_,&w)| w == self.focused_window).map(|(i,_)| i).unwrap();
    let new_focused_window = match op {
      Up => {
        if index == 0 {
          windows[count - 1]
        } else {
          windows[index - 1]
        }
      },
      Down => {
        windows[(index + 1) % count]
      },
      Swap => {
        windows[0]
      }
    };

    self.focus_window(ws, config, new_focused_window);
  }

  pub fn move_window(&mut self, ws: &XlibWindowSystem, config: &Config, op: MoveOp) {
    if self.focused_window == 0 || self.managed.len() < 2 {
      return;
    }

    let pos = self.managed.iter().enumerate().find(|&(_,&w)| w == self.focused_window).map(|(i,_)| i).unwrap();
    let new_pos = match op {
      Up => {
        if pos == 0 {
          self.managed.len() - 1
        } else {
          pos - 1
        }
      },
      Down => {
        (pos + 1) % self.managed.len()
      },
      Swap => {
        let master = self.managed[0];
        self.managed.insert(pos, master);
        self.managed.remove(0);
        0
      }
    };

    self.managed.remove(pos);
    self.managed.insert(new_pos, self.focused_window);

    self.redraw(ws, config);
  }

  pub fn index_of(&self, window: Window) -> Option<uint> {
    self.managed.iter().chain(self.unmanaged.iter()).enumerate().find(|&(_,&w)| w == window).map(|(i,_)| i)
  }

  pub fn contains(&self, window: Window) -> bool {
    self.managed.iter().chain(self.unmanaged.iter()).any(|&w| w == window)
  }

  pub fn get_focused_window(&self) -> Window {
    self.focused_window
  }

  pub fn unfocus(&mut self, ws: &XlibWindowSystem, config: &Config) {
    ws.set_window_border_color(self.focused_window, config.border_color);
  }

  pub fn focus(&self, ws: &XlibWindowSystem, config: &Config) {
    if self.focused_window != 0 {
      ws.focus_window(self.focused_window, config.border_focus_color);
    }
  }

  pub fn hide(&mut self, ws: &XlibWindowSystem) {
    self.visible = false;

    for &w in self.managed.iter() {
      ws.unmap_window(w);
    }

    for &w in self.unmanaged.iter() {
      ws.unmap_window(w);
    }
  }

  pub fn show(&mut self, ws: &XlibWindowSystem, config: &Config) {
    self.visible = true;

    self.redraw(ws, config);
    for &w in self.managed.iter() {
      ws.map_window(w);
    }

    for &w in self.unmanaged.iter() {
      ws.map_window(w);
    }
  }

  pub fn redraw(&self, ws: &XlibWindowSystem, config: &Config) {
    debug!("Redraw");
    let screen = ws.get_screen_infos()[self.screen];

    for (i,rect) in self.layout.apply(screen, &self.managed).iter().enumerate() {
      ws.setup_window(rect.x, rect.y, rect.width, rect.height, config.border_width, config.border_color, self.managed[i]);
    }

    for &window in self.unmanaged.iter() {
      let mut rect = ws.get_geometry(window);
      rect.width = rect.width + (2 * config.border_width);
      rect.height = rect.height + (2 * config.border_width);

      ws.setup_window((screen.width - rect.width) / 2, (screen.height - rect.height) / 2, rect.width, rect.height, config.border_width, config.border_color, window);
    }
    self.focus(ws, config);
  }
}

pub struct Workspaces {
  list: Vec<Workspace>,
  cur: uint
}

impl Workspaces {
  pub fn new(config: &mut Config, screens: uint) -> Workspaces{
    let mut workspaces = Workspaces{
      list: config.workspaces.iter_mut().map(|c| {
        Workspace {
          managed: Vec::new(),
          unmanaged: Vec::new(),
          focused_window: 0,
          tag: c.tag.clone(),
          screen: c.screen,
          visible: false,
          layout: (c.layout)()
        }
      }).collect(),
      cur: 0,
    };

    for screen in range(0u, screens) {
      if workspaces.list.iter().find(|ws| ws.screen == screen).is_none() {
        match workspaces.list.iter_mut().filter(|ws| ws.screen == 0).nth(1) {
          Some(ws) => {
            ws.screen = screen;
          },
          None => {}
        }
      }
    }

    for screen in range(0u, screens) {
      let ws = workspaces.list.iter_mut().find(|ws| ws.screen == screen).unwrap();
      ws.visible = true;
    }

    workspaces
  }

  pub fn get(&self, index: uint) -> &Workspace {
    if index < self.list.len() {
      self.list.get(index).unwrap()
    } else {
      self.current()
    }
  }

  pub fn get_mut(&mut self, index: uint) -> &mut Workspace {
    if index < self.list.len() {
      self.list.get_mut(index).unwrap()
    } else {
      self.current_mut()
    }
  }

  pub fn current(&self) -> &Workspace {
    self.list.get(self.cur).unwrap()
  }

   pub fn current_mut(&mut self) -> &mut Workspace {
    self.list.get_mut(self.cur).unwrap()
  }

  pub fn all(&self) -> &Vec<Workspace> {
    &self.list
  }

  pub fn get_index(&self) -> uint {
    self.cur
  }

  pub fn switch_to(&mut self, ws: &XlibWindowSystem, config: &Config, index: uint) {
    if self.cur != index && index < self.list.len() {
      if self.list[index].visible {
        if config.greedy_view {
          self.switch_screens(index);
          self.list[self.cur].show(ws, config);
        }
      } else {
        self.list[index].screen = self.list[self.cur].screen;
        self.list[self.cur].hide(ws);
      }

      self.list[self.cur].unfocus(ws, config);
      self.list[index].show(ws, config);
      self.list[index].focus(ws, config);
      self.cur = index;
    }
  }

  pub fn switch_to_screen(&mut self, ws: &XlibWindowSystem, config: &Config, screen: uint) {
    match self.list.iter().enumerate().filter(|&(_,ws)| ws.screen == screen && ws.visible).map(|(i,_)| i).last() {
      Some(index) => {
        self.list[self.cur].unfocus(ws, config);
        self.list[index].focus(ws, config);
        self.cur = index;
      },
      None => { }
    }
  }

  pub fn move_window_to(&mut self, ws: &XlibWindowSystem, config: &Config, index: uint) {
    let window = self.list[self.cur].get_focused_window();
    if window == 0 {
      return;
    }

    self.remove_window(ws, config, window);
    self.list[index].add_window(ws, config, window);
    self.list[index].unfocus(ws, config);
  }

  pub fn move_window_to_screen(&mut self, ws: &XlibWindowSystem, config: &Config, screen: uint) {
    match self.list.iter().enumerate().find(|&(_,ws)| ws.screen == screen) {
      Some((index,_)) => {
        self.move_window_to(ws, config, index);
      },
      None => {}
    }
  }

  pub fn remove_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    match self.list.iter_mut().find(|workspace| workspace.contains(window)) {
      Some(workspace) => {
        workspace.remove_window(ws, config, window);
      },
      None => {}
    }
  }

  pub fn rescreen(&mut self, ws: &XlibWindowSystem, config: &Config) {
    let new_screens = ws.get_screen_infos().len();

    // move and hide workspaces if their screens got removed
    for workspace in self.list.iter_mut().filter(|ws| ws.screen >= new_screens) {
      workspace.screen = 0;
      workspace.hide(ws);
    }

    // assign the first hidden workspace to the new screen
    let prev_screens = self.list.iter().fold(0, |acc, x| cmp::max(acc, x.screen));
    for screen in range(prev_screens, new_screens) {
      match self.list.iter_mut().find(|ws| !ws.visible) {
        Some(workspace) => {
          workspace.screen = screen;
          workspace.show(ws, config);
        },
        None => {
          break;
        }
      }
    }
  }

  pub fn contains(&self, window: Window) -> bool {
    self.list.iter().any(|ws| ws.contains(window))
  }

  fn switch_screens(&mut self, dest: uint) {
    let screen = self.list[self.cur].screen;
    self.list[self.cur].screen = self.list[dest].screen;
    self.list[dest].screen = screen;
  }
}<commit_msg>removed index_of function<commit_after>use config::Config;
use layout::{Layout, LayoutBox};
use xlib::Window;
use xlib_window_system::XlibWindowSystem;
use self::MoveOp::*;
use std::cmp;

pub struct WorkspaceConfig {
  pub tag: String,
  pub screen: uint,
  pub layout: LayoutBox
}

pub struct Workspace {
  managed: Vec<Window>,
  unmanaged: Vec<Window>,
  focused_window: Window,
  tag: String,
  screen: uint,
  visible: bool,
  layout: Box<Layout + 'static>
}

pub enum MoveOp {
  Up,
  Down,
  Swap
}

impl Workspace {
  pub fn add_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if !ws.is_window_floating(window) {
      self.managed.push(window);

      if self.unmanaged.len() > 0 {
        ws.restack_windows(self.unmanaged.iter_mut().chain(self.managed.iter_mut()).map(|&x| x).collect());
      }
    } else {
      self.unmanaged.push(window);
    }

    self.focus_window(ws, config, window);
    if self.visible {
      self.redraw(ws, config);
      ws.map_window(window);
    }
  }

  fn is_managed(&self, window: Window) -> bool {
    self.managed.iter().any(|&x| x == window)
  }

  fn is_unmanaged(&self, window: Window) -> bool {
    self.unmanaged.iter().any(|&x| x == window)
  }

  fn get_managed(&self, window: Window) -> uint {
    self.managed.iter().enumerate().filter(|&(_,&w)| w == window).map(|(i,_)| i).last().unwrap()
  }

  fn get_unmanaged(&self, window: Window) -> uint {
    self.unmanaged.iter().enumerate().filter(|&(_,&w)| w == window).map(|(i,_)| i).last().unwrap()
  }

  pub fn get_layout(&self) -> &Box<Layout + 'static> {
    &self.layout
  }

  pub fn get_tag(&self) -> String {
    self.tag.clone()
  }

  fn remove_managed(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    let index = self.get_managed(window);

    self.focused_window = 0;
    ws.unmap_window(window);
    self.managed.remove(index);

    let new_focused_window = if !self.managed.is_empty() {
      self.managed[if index < self.managed.len() { index } else { index - 1}]
    } else {
      0
    };

    if self.visible {
      self.redraw(ws, config);
      self.focus_window(ws, config, new_focused_window);
    }
  }

  fn remove_unmanaged(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    let index = self.get_unmanaged(window);

    self.focused_window = 0;
    ws.unmap_window(window);
    self.unmanaged.remove(index);

    let new_focused_window = if !self.unmanaged.is_empty() {
      self.unmanaged[if index < self.unmanaged.len() { index } else { index - 1}]
    } else {
      0
    };

    if self.visible {
      self.redraw(ws, config);
      self.focus_window(ws, config, new_focused_window);
    }
  }

  pub fn remove_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if self.is_managed(window) {
      self.remove_managed(ws, config, window);
    } else {
      if self.is_unmanaged(window) {
        self.remove_unmanaged(ws, config, window);
      }
    }
  }

  pub fn focus_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    if window == 0 {
      return;
    }

    self.unfocus_window(ws, config);
    self.focused_window = window;
    ws.focus_window(window, config.border_focus_color);
  }

  pub fn unfocus_window(&mut self, ws: &XlibWindowSystem, config: &Config) {
    if self.focused_window != 0 {
      ws.set_window_border_color(self.focused_window, config.border_color);
      self.focused_window = 0;
    }
  }

  pub fn move_focus(&mut self, ws: &XlibWindowSystem, config: &Config, op: MoveOp) {
    let windows : Vec<Window> = self.managed.iter().chain(self.unmanaged.iter()).map(|&x| x).collect();
    let count = windows.len();

    if self.focused_window == 0 || count < 2 {
      return;
    }

    let index = windows.iter().enumerate().find(|&(_,&w)| w == self.focused_window).map(|(i,_)| i).unwrap();
    let new_focused_window = match op {
      Up => {
        if index == 0 {
          windows[count - 1]
        } else {
          windows[index - 1]
        }
      },
      Down => {
        windows[(index + 1) % count]
      },
      Swap => {
        windows[0]
      }
    };

    self.focus_window(ws, config, new_focused_window);
  }

  pub fn move_window(&mut self, ws: &XlibWindowSystem, config: &Config, op: MoveOp) {
    if self.focused_window == 0 || self.managed.len() < 2 {
      return;
    }

    let pos = self.managed.iter().enumerate().find(|&(_,&w)| w == self.focused_window).map(|(i,_)| i).unwrap();
    let new_pos = match op {
      Up => {
        if pos == 0 {
          self.managed.len() - 1
        } else {
          pos - 1
        }
      },
      Down => {
        (pos + 1) % self.managed.len()
      },
      Swap => {
        let master = self.managed[0];
        self.managed.insert(pos, master);
        self.managed.remove(0);
        0
      }
    };

    self.managed.remove(pos);
    self.managed.insert(new_pos, self.focused_window);

    self.redraw(ws, config);
  }

  pub fn contains(&self, window: Window) -> bool {
    self.managed.iter().chain(self.unmanaged.iter()).any(|&w| w == window)
  }

  pub fn get_focused_window(&self) -> Window {
    self.focused_window
  }

  pub fn unfocus(&mut self, ws: &XlibWindowSystem, config: &Config) {
    ws.set_window_border_color(self.focused_window, config.border_color);
  }

  pub fn focus(&self, ws: &XlibWindowSystem, config: &Config) {
    if self.focused_window != 0 {
      ws.focus_window(self.focused_window, config.border_focus_color);
    }
  }

  pub fn hide(&mut self, ws: &XlibWindowSystem) {
    self.visible = false;

    for &w in self.managed.iter() {
      ws.unmap_window(w);
    }

    for &w in self.unmanaged.iter() {
      ws.unmap_window(w);
    }
  }

  pub fn show(&mut self, ws: &XlibWindowSystem, config: &Config) {
    self.visible = true;

    self.redraw(ws, config);
    for &w in self.managed.iter() {
      ws.map_window(w);
    }

    for &w in self.unmanaged.iter() {
      ws.map_window(w);
    }
  }

  pub fn redraw(&self, ws: &XlibWindowSystem, config: &Config) {
    debug!("Redraw");
    let screen = ws.get_screen_infos()[self.screen];

    for (i,rect) in self.layout.apply(screen, &self.managed).iter().enumerate() {
      ws.setup_window(rect.x, rect.y, rect.width, rect.height, config.border_width, config.border_color, self.managed[i]);
    }

    for &window in self.unmanaged.iter() {
      let mut rect = ws.get_geometry(window);
      rect.width = rect.width + (2 * config.border_width);
      rect.height = rect.height + (2 * config.border_width);

      ws.setup_window((screen.width - rect.width) / 2, (screen.height - rect.height) / 2, rect.width, rect.height, config.border_width, config.border_color, window);
    }
    self.focus(ws, config);
  }
}

pub struct Workspaces {
  list: Vec<Workspace>,
  cur: uint
}

impl Workspaces {
  pub fn new(config: &mut Config, screens: uint) -> Workspaces{
    let mut workspaces = Workspaces{
      list: config.workspaces.iter_mut().map(|c| {
        Workspace {
          managed: Vec::new(),
          unmanaged: Vec::new(),
          focused_window: 0,
          tag: c.tag.clone(),
          screen: c.screen,
          visible: false,
          layout: (c.layout)()
        }
      }).collect(),
      cur: 0,
    };

    for screen in range(0u, screens) {
      if workspaces.list.iter().find(|ws| ws.screen == screen).is_none() {
        match workspaces.list.iter_mut().filter(|ws| ws.screen == 0).nth(1) {
          Some(ws) => {
            ws.screen = screen;
          },
          None => {}
        }
      }
    }

    for screen in range(0u, screens) {
      let ws = workspaces.list.iter_mut().find(|ws| ws.screen == screen).unwrap();
      ws.visible = true;
    }

    workspaces
  }

  pub fn get(&self, index: uint) -> &Workspace {
    if index < self.list.len() {
      self.list.get(index).unwrap()
    } else {
      self.current()
    }
  }

  pub fn get_mut(&mut self, index: uint) -> &mut Workspace {
    if index < self.list.len() {
      self.list.get_mut(index).unwrap()
    } else {
      self.current_mut()
    }
  }

  pub fn current(&self) -> &Workspace {
    self.list.get(self.cur).unwrap()
  }

   pub fn current_mut(&mut self) -> &mut Workspace {
    self.list.get_mut(self.cur).unwrap()
  }

  pub fn all(&self) -> &Vec<Workspace> {
    &self.list
  }

  pub fn get_index(&self) -> uint {
    self.cur
  }

  pub fn switch_to(&mut self, ws: &XlibWindowSystem, config: &Config, index: uint) {
    if self.cur != index && index < self.list.len() {
      if self.list[index].visible {
        if config.greedy_view {
          self.switch_screens(index);
          self.list[self.cur].show(ws, config);
        }
      } else {
        self.list[index].screen = self.list[self.cur].screen;
        self.list[self.cur].hide(ws);
      }

      self.list[self.cur].unfocus(ws, config);
      self.list[index].show(ws, config);
      self.list[index].focus(ws, config);
      self.cur = index;
    }
  }

  pub fn switch_to_screen(&mut self, ws: &XlibWindowSystem, config: &Config, screen: uint) {
    match self.list.iter().enumerate().filter(|&(_,ws)| ws.screen == screen && ws.visible).map(|(i,_)| i).last() {
      Some(index) => {
        self.list[self.cur].unfocus(ws, config);
        self.list[index].focus(ws, config);
        self.cur = index;
      },
      None => { }
    }
  }

  pub fn move_window_to(&mut self, ws: &XlibWindowSystem, config: &Config, index: uint) {
    let window = self.list[self.cur].get_focused_window();
    if window == 0 {
      return;
    }

    self.remove_window(ws, config, window);
    self.list[index].add_window(ws, config, window);
    self.list[index].unfocus(ws, config);
  }

  pub fn move_window_to_screen(&mut self, ws: &XlibWindowSystem, config: &Config, screen: uint) {
    match self.list.iter().enumerate().find(|&(_,ws)| ws.screen == screen) {
      Some((index,_)) => {
        self.move_window_to(ws, config, index);
      },
      None => {}
    }
  }

  pub fn remove_window(&mut self, ws: &XlibWindowSystem, config: &Config, window: Window) {
    match self.list.iter_mut().find(|workspace| workspace.contains(window)) {
      Some(workspace) => {
        workspace.remove_window(ws, config, window);
      },
      None => {}
    }
  }

  pub fn rescreen(&mut self, ws: &XlibWindowSystem, config: &Config) {
    let new_screens = ws.get_screen_infos().len();

    // move and hide workspaces if their screens got removed
    for workspace in self.list.iter_mut().filter(|ws| ws.screen >= new_screens) {
      workspace.screen = 0;
      workspace.hide(ws);
    }

    // assign the first hidden workspace to the new screen
    let prev_screens = self.list.iter().fold(0, |acc, x| cmp::max(acc, x.screen));
    for screen in range(prev_screens, new_screens) {
      match self.list.iter_mut().find(|ws| !ws.visible) {
        Some(workspace) => {
          workspace.screen = screen;
          workspace.show(ws, config);
        },
        None => {
          break;
        }
      }
    }
  }

  pub fn contains(&self, window: Window) -> bool {
    self.list.iter().any(|ws| ws.contains(window))
  }

  fn switch_screens(&mut self, dest: uint) {
    let screen = self.list[self.cur].screen;
    self.list[self.cur].screen = self.list[dest].screen;
    self.list[dest].screen = screen;
  }
}<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]
#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate clap;
#[cfg(feature = "dbus_enabled")]
extern crate dbus;
extern crate env_logger;
extern crate libc;
extern crate libudev;
extern crate nix;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{ErrorKind, Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg, ArgMatches};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature = "dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: &StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device.devnode().and_then(|devnode| {
            device
                .devnum()
                .and_then(|devnum| Some((Device::from(devnum), PathBuf::from(devnode))))
        });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn trylock_pid_file() -> StratisResult<File> {
    let mut f = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)
    {
        Ok(f) => f,
        Err(e) => {
            if e.kind() == ErrorKind::PermissionDenied {
                return Err(StratisError::Error(
                    "Must be running as root in order to start daemon.".to_string(),
                ));
            }
            return Err(e.into());
        }
    };
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write_all(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!(
                "Daemon already running with pid: {}",
                pid_str
            )))
        }
    }
}

fn run(matches: &ArgMatches) -> StratisResult<()> {
    initialize_log(matches.is_present("debug"))
        .expect("This is the first and only invocation of this method; it must succeed.");

    // Setup a udev listener before initializing the engine. A device may
    // appear after the engine has read the /dev directory but before it has
    // completed initialization. Unless the udev event has been recorded, the
    // engine will miss the device.
    // This is especially important since stratisd must run during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
        fd: udev.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                fd: evt.get_pollable_fd(),
                revents: 0,
                events: libc::POLLIN,
            });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature = "dbus_enabled")]
    let (dbus_conn, mut tree, base_object_path, dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            while let Some(event) = udev.receive_event() {
                if let Some((device, devnode)) = handle_udev_add(&event) {
                    // If block evaluate returns an error we are going to ignore it as
                    // there is nothing we can do for a device we are getting errors with.
                    let pool_uuid = engine
                        .borrow_mut()
                        .block_evaluate(device, devnode)
                        .unwrap_or(None);

                    // We need to pretend that we aren't using the variable _pool_uuid so
                    // that we can conditionally compile out the register_pool when dbus
                    // is not enabled.
                    if let Some(_pool_uuid) = pool_uuid {
                        #[cfg(feature = "dbus_enabled")]
                        libstratis::dbus_api::register_pool(
                            &dbus_conn,
                            &Rc::clone(&engine),
                            &dbus_context,
                            &mut tree,
                            _pool_uuid,
                            &base_object_path,
                        )?;
                    }
                }
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature = "dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                .iter()
                .filter(|pfd| pfd.revents != 0)
            {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) =
                        libstratis::dbus_api::handle(&dbus_conn, &item, &mut tree, &dbus_context)
                    {
                        print_err(&From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(
            Arg::with_name("debug")
                .long("debug")
                .help("Print additional output for debugging"),
        )
        .arg(
            Arg::with_name("sim")
                .long("sim")
                .help("Use simulator engine"),
        )
        .get_matches();

    let result = trylock_pid_file().and_then(|_pidfile| run(&matches));
    if let Err(err) = result {
        print_err(&err);
        exit(1);
    } else {
        exit(0);
    }
}
<commit_msg>Initialize the log in the main method<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]
#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate clap;
#[cfg(feature = "dbus_enabled")]
extern crate dbus;
extern crate env_logger;
extern crate libc;
extern crate libudev;
extern crate nix;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{ErrorKind, Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg, ArgMatches};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature = "dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: &StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device.devnode().and_then(|devnode| {
            device
                .devnum()
                .and_then(|devnum| Some((Device::from(devnum), PathBuf::from(devnode))))
        });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn trylock_pid_file() -> StratisResult<File> {
    let mut f = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)
    {
        Ok(f) => f,
        Err(e) => {
            if e.kind() == ErrorKind::PermissionDenied {
                return Err(StratisError::Error(
                    "Must be running as root in order to start daemon.".to_string(),
                ));
            }
            return Err(e.into());
        }
    };
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write_all(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!(
                "Daemon already running with pid: {}",
                pid_str
            )))
        }
    }
}

fn run(matches: &ArgMatches) -> StratisResult<()> {
    // Setup a udev listener before initializing the engine. A device may
    // appear after the engine has read the /dev directory but before it has
    // completed initialization. Unless the udev event has been recorded, the
    // engine will miss the device.
    // This is especially important since stratisd must run during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
        fd: udev.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                fd: evt.get_pollable_fd(),
                revents: 0,
                events: libc::POLLIN,
            });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature = "dbus_enabled")]
    let (dbus_conn, mut tree, base_object_path, dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            while let Some(event) = udev.receive_event() {
                if let Some((device, devnode)) = handle_udev_add(&event) {
                    // If block evaluate returns an error we are going to ignore it as
                    // there is nothing we can do for a device we are getting errors with.
                    let pool_uuid = engine
                        .borrow_mut()
                        .block_evaluate(device, devnode)
                        .unwrap_or(None);

                    // We need to pretend that we aren't using the variable _pool_uuid so
                    // that we can conditionally compile out the register_pool when dbus
                    // is not enabled.
                    if let Some(_pool_uuid) = pool_uuid {
                        #[cfg(feature = "dbus_enabled")]
                        libstratis::dbus_api::register_pool(
                            &dbus_conn,
                            &Rc::clone(&engine),
                            &dbus_context,
                            &mut tree,
                            _pool_uuid,
                            &base_object_path,
                        )?;
                    }
                }
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature = "dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                .iter()
                .filter(|pfd| pfd.revents != 0)
            {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) =
                        libstratis::dbus_api::handle(&dbus_conn, &item, &mut tree, &dbus_context)
                    {
                        print_err(&From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(
            Arg::with_name("debug")
                .long("debug")
                .help("Print additional output for debugging"),
        )
        .arg(
            Arg::with_name("sim")
                .long("sim")
                .help("Use simulator engine"),
        )
        .get_matches();

    let result = trylock_pid_file()
        .and_then(|_pidfile| {
            Ok(initialize_log(matches.is_present("debug"))
                .expect("This is the first and only invocation of this method; it must succeed."))
        })
        .and_then(|_| run(&matches));
    if let Err(err) = result {
        print_err(&err);
        exit(1);
    } else {
        exit(0);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::rc::Rc;
use std::hash::{self, Hash, Hasher};
use std::iter::repeat;

use syntax::ast::Expr;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use rand::{Rng, SeedableRng, XorShiftRng};

use phf_shared::{self, PhfHash};

const DEFAULT_LAMBDA: usize = 5;

const FIXED_SEED: [u32; 4] = [3141592653, 589793238, 462643383, 2795028841];

#[derive(PartialEq, Eq, Clone)]
pub enum Key {
    Str(InternedString),
    Binary(Rc<Vec<u8>>),
    Char(char),
    U8(u8),
    I8(i8),
    U16(u16),
    I16(i16),
    U32(u32),
    I32(i32),
    U64(u64),
    I64(i64),
    Bool(bool),
}

impl<S> Hash<S> for Key where S: Hasher + hash::Writer {
    fn hash(&self, state: &mut S) {
        match *self {
            Key::Str(ref s) => s.get().hash(state),
            Key::Binary(ref b) => b.hash(state),
            Key::Char(c) => c.hash(state),
            Key::U8(b) => b.hash(state),
            Key::I8(b) => b.hash(state),
            Key::U16(b) => b.hash(state),
            Key::I16(b) => b.hash(state),
            Key::U32(b) => b.hash(state),
            Key::I32(b) => b.hash(state),
            Key::U64(b) => b.hash(state),
            Key::I64(b) => b.hash(state),
            Key::Bool(b) => b.hash(state),
        }
    }
}

impl PhfHash for Key {
    fn phf_hash(&self, key: u64) -> (u32, u32, u32) {
        match *self {
            Key::Str(ref s) => s.get().phf_hash(key),
            Key::Binary(ref b) => b.phf_hash(key),
            Key::Char(c) => c.phf_hash(key),
            Key::U8(b) => b.phf_hash(key),
            Key::I8(b) => b.phf_hash(key),
            Key::U16(b) => b.phf_hash(key),
            Key::I16(b) => b.phf_hash(key),
            Key::U32(b) => b.phf_hash(key),
            Key::I32(b) => b.phf_hash(key),
            Key::U64(b) => b.phf_hash(key),
            Key::I64(b) => b.phf_hash(key),
            Key::Bool(b) => b.phf_hash(key),
        }
    }
}

pub struct Entry {
    pub key_contents: Key,
    pub key: P<Expr>,
    pub value: P<Expr>
}

pub struct HashState {
    key: u64,
    disps: Vec<(u32, u32)>,
    map: Vec<usize>,
}

pub fn generate_hash(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> HashState {
    #[cfg(feature = "stats")]
    use time::precise_time_s;
    #[cfg(not(feature = "stats"))]
    fn precise_time_s() -> f64 { 0.0 }

    let mut rng: XorShiftRng = SeedableRng::from_seed(FIXED_SEED);
    let start = precise_time_s();
    let state;
    loop {
        match try_generate_hash(entries, &mut rng) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = precise_time_s() - start;
    if cfg!(feature = "stats") && env::var("PHF_STATS").is_some() {
        cx.span_note(sp, &*format!("PHF generation took {} seconds", time));
    }

    state
}

pub fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng) -> Option<HashState> {
    struct Bucket {
        idx: usize,
        keys: Vec<usize>,
    }

    struct Hashes {
        g: u32,
        f1: u32,
        f2: u32,
    }

    let key = rng.gen();

    let hashes: Vec<_> = entries.iter().map(|entry| {
        let (g, f1, f2) = entry.key_contents.phf_hash(key);
        Hashes {
            g: g,
            f1: f1,
            f2: f2
        }
    }).collect();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = range(0, buckets_len)
        .map(|i| Bucket { idx: i, keys: vec![] })
        .collect::<Vec<_>>();

    for (i, hash) in hashes.iter().enumerate() {
        buckets[(hash.g % (buckets_len as u32)) as usize].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| a.keys.len().cmp(&b.keys.len()).reverse());

    let table_len = entries.len();
    let mut map = repeat(None).take(table_len).collect::<Vec<_>>();
    let mut disps = repeat((0u32, 0u32)).take(buckets_len).collect::<Vec<_>>();

    // store whether an element from the bucket being placed is
    // located at a certain position, to allow for efficient overlap
    // checks. It works by storing the generation in each cell and
    // each new placement-attempt is a new generation, so you can tell
    // if this is legitimately full by checking that the generations
    // are equal. (A u64 is far too large to overflow in a reasonable
    // time for current hardware.)
    let mut try_map = repeat(0u64).take(table_len).collect::<Vec<_>>();
    let mut generation = 0u64;

    // the actual values corresponding to the markers above, as
    // (index, key) pairs, for adding to the main map once we've
    // chosen the right disps.
    let mut values_to_add = vec![];

    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len as u32) {
            'disps: for d2 in range(0, table_len as u32) {
                values_to_add.clear();
                generation += 1;

                for &key in bucket.keys.iter() {
                    let idx = (phf_shared::displace(hashes[key].f1, hashes[key].f2, d1, d2)
                                % (table_len as u32)) as usize;
                    if map[idx].is_some() || try_map[idx] == generation {
                        continue 'disps;
                    }
                    try_map[idx] = generation;
                    values_to_add.push((idx, key));
                }

                // We've picked a good set of disps
                disps[bucket.idx] = (d1, d2);
                for &(idx, key) in values_to_add.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    Some(HashState {
        key: key,
        disps: disps,
        map: map.into_iter().map(|i| i.unwrap()).collect(),
    })
}

pub fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                  -> Box<MacResult+'static> {
    let disps = state.disps.iter().map(|&(d1, d2)| {
        quote_expr!(&*cx, ($d1, $d2))
    }).collect();
    let disps = cx.expr_vec(sp, disps);

    let entries = state.map.iter().map(|&idx| {
        let &Entry { ref key, ref value, .. } = &entries[idx];
        quote_expr!(&*cx, ($key, $value))
    }).collect();
    let entries = cx.expr_vec(sp, entries);

    let key = state.key;
    MacExpr::new(quote_expr!(cx, ::phf::Map {
        key: $key,
        disps: &$disps,
        entries: &$entries,
    }))
}

pub fn create_set(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
              -> Box<MacResult+'static> {
    let map = create_map(cx, sp, entries, state).make_expr().unwrap();
    MacExpr::new(quote_expr!(cx, ::phf::Set { map: $map }))
}

pub fn create_ordered_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                          -> Box<MacResult+'static> {
    let disps = state.disps.iter().map(|&(d1, d2)| {
        quote_expr!(&*cx, ($d1, $d2))
    }).collect();
    let disps = cx.expr_vec(sp, disps);

    let idxs = state.map.iter().map(|&idx| quote_expr!(&*cx, $idx)).collect();
    let idxs = cx.expr_vec(sp, idxs);

    let entries = entries.iter().map(|&Entry { ref key, ref value, .. }| {
        quote_expr!(&*cx, ($key, $value))
    }).collect();
    let entries = cx.expr_vec(sp, entries);

    let key = state.key;
    MacExpr::new(quote_expr!(cx, ::phf::OrderedMap {
        key: $key,
        disps: &$disps,
        idxs: &$idxs,
        entries: &$entries,
    }))
}

pub fn create_ordered_set(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                          -> Box<MacResult+'static> {
    let map = create_ordered_map(cx, sp, entries, state).make_expr().unwrap();
    MacExpr::new(quote_expr!(cx, ::phf::OrderedSet { map: $map }))
}
<commit_msg>InternedString.get() removal; brings us to rustc 1.0.0-dev (80627cd3c 2015-02-07 12:01:31 +0000)<commit_after>use std::env;
use std::rc::Rc;
use std::hash::{self, Hash, Hasher};
use std::iter::repeat;

use syntax::ast::Expr;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token::InternedString;
use syntax::ptr::P;
use rand::{Rng, SeedableRng, XorShiftRng};

use phf_shared::{self, PhfHash};

const DEFAULT_LAMBDA: usize = 5;

const FIXED_SEED: [u32; 4] = [3141592653, 589793238, 462643383, 2795028841];

#[derive(PartialEq, Eq, Clone)]
pub enum Key {
    Str(InternedString),
    Binary(Rc<Vec<u8>>),
    Char(char),
    U8(u8),
    I8(i8),
    U16(u16),
    I16(i16),
    U32(u32),
    I32(i32),
    U64(u64),
    I64(i64),
    Bool(bool),
}

impl<S> Hash<S> for Key where S: Hasher + hash::Writer {
    fn hash(&self, state: &mut S) {
        match *self {
            Key::Str(ref s) => s.hash(state),
            Key::Binary(ref b) => b.hash(state),
            Key::Char(c) => c.hash(state),
            Key::U8(b) => b.hash(state),
            Key::I8(b) => b.hash(state),
            Key::U16(b) => b.hash(state),
            Key::I16(b) => b.hash(state),
            Key::U32(b) => b.hash(state),
            Key::I32(b) => b.hash(state),
            Key::U64(b) => b.hash(state),
            Key::I64(b) => b.hash(state),
            Key::Bool(b) => b.hash(state),
        }
    }
}

impl PhfHash for Key {
    fn phf_hash(&self, key: u64) -> (u32, u32, u32) {
        match *self {
            Key::Str(ref s) => s.phf_hash(key),
            Key::Binary(ref b) => b.phf_hash(key),
            Key::Char(c) => c.phf_hash(key),
            Key::U8(b) => b.phf_hash(key),
            Key::I8(b) => b.phf_hash(key),
            Key::U16(b) => b.phf_hash(key),
            Key::I16(b) => b.phf_hash(key),
            Key::U32(b) => b.phf_hash(key),
            Key::I32(b) => b.phf_hash(key),
            Key::U64(b) => b.phf_hash(key),
            Key::I64(b) => b.phf_hash(key),
            Key::Bool(b) => b.phf_hash(key),
        }
    }
}

pub struct Entry {
    pub key_contents: Key,
    pub key: P<Expr>,
    pub value: P<Expr>
}

pub struct HashState {
    key: u64,
    disps: Vec<(u32, u32)>,
    map: Vec<usize>,
}

pub fn generate_hash(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> HashState {
    #[cfg(feature = "stats")]
    use time::precise_time_s;
    #[cfg(not(feature = "stats"))]
    fn precise_time_s() -> f64 { 0.0 }

    let mut rng: XorShiftRng = SeedableRng::from_seed(FIXED_SEED);
    let start = precise_time_s();
    let state;
    loop {
        match try_generate_hash(entries, &mut rng) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = precise_time_s() - start;
    if cfg!(feature = "stats") && env::var("PHF_STATS").is_some() {
        cx.span_note(sp, &*format!("PHF generation took {} seconds", time));
    }

    state
}

pub fn try_generate_hash(entries: &[Entry], rng: &mut XorShiftRng) -> Option<HashState> {
    struct Bucket {
        idx: usize,
        keys: Vec<usize>,
    }

    struct Hashes {
        g: u32,
        f1: u32,
        f2: u32,
    }

    let key = rng.gen();

    let hashes: Vec<_> = entries.iter().map(|entry| {
        let (g, f1, f2) = entry.key_contents.phf_hash(key);
        Hashes {
            g: g,
            f1: f1,
            f2: f2
        }
    }).collect();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = range(0, buckets_len)
        .map(|i| Bucket { idx: i, keys: vec![] })
        .collect::<Vec<_>>();

    for (i, hash) in hashes.iter().enumerate() {
        buckets[(hash.g % (buckets_len as u32)) as usize].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| a.keys.len().cmp(&b.keys.len()).reverse());

    let table_len = entries.len();
    let mut map = repeat(None).take(table_len).collect::<Vec<_>>();
    let mut disps = repeat((0u32, 0u32)).take(buckets_len).collect::<Vec<_>>();

    // store whether an element from the bucket being placed is
    // located at a certain position, to allow for efficient overlap
    // checks. It works by storing the generation in each cell and
    // each new placement-attempt is a new generation, so you can tell
    // if this is legitimately full by checking that the generations
    // are equal. (A u64 is far too large to overflow in a reasonable
    // time for current hardware.)
    let mut try_map = repeat(0u64).take(table_len).collect::<Vec<_>>();
    let mut generation = 0u64;

    // the actual values corresponding to the markers above, as
    // (index, key) pairs, for adding to the main map once we've
    // chosen the right disps.
    let mut values_to_add = vec![];

    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len as u32) {
            'disps: for d2 in range(0, table_len as u32) {
                values_to_add.clear();
                generation += 1;

                for &key in bucket.keys.iter() {
                    let idx = (phf_shared::displace(hashes[key].f1, hashes[key].f2, d1, d2)
                                % (table_len as u32)) as usize;
                    if map[idx].is_some() || try_map[idx] == generation {
                        continue 'disps;
                    }
                    try_map[idx] = generation;
                    values_to_add.push((idx, key));
                }

                // We've picked a good set of disps
                disps[bucket.idx] = (d1, d2);
                for &(idx, key) in values_to_add.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    Some(HashState {
        key: key,
        disps: disps,
        map: map.into_iter().map(|i| i.unwrap()).collect(),
    })
}

pub fn create_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                  -> Box<MacResult+'static> {
    let disps = state.disps.iter().map(|&(d1, d2)| {
        quote_expr!(&*cx, ($d1, $d2))
    }).collect();
    let disps = cx.expr_vec(sp, disps);

    let entries = state.map.iter().map(|&idx| {
        let &Entry { ref key, ref value, .. } = &entries[idx];
        quote_expr!(&*cx, ($key, $value))
    }).collect();
    let entries = cx.expr_vec(sp, entries);

    let key = state.key;
    MacExpr::new(quote_expr!(cx, ::phf::Map {
        key: $key,
        disps: &$disps,
        entries: &$entries,
    }))
}

pub fn create_set(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
              -> Box<MacResult+'static> {
    let map = create_map(cx, sp, entries, state).make_expr().unwrap();
    MacExpr::new(quote_expr!(cx, ::phf::Set { map: $map }))
}

pub fn create_ordered_map(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                          -> Box<MacResult+'static> {
    let disps = state.disps.iter().map(|&(d1, d2)| {
        quote_expr!(&*cx, ($d1, $d2))
    }).collect();
    let disps = cx.expr_vec(sp, disps);

    let idxs = state.map.iter().map(|&idx| quote_expr!(&*cx, $idx)).collect();
    let idxs = cx.expr_vec(sp, idxs);

    let entries = entries.iter().map(|&Entry { ref key, ref value, .. }| {
        quote_expr!(&*cx, ($key, $value))
    }).collect();
    let entries = cx.expr_vec(sp, entries);

    let key = state.key;
    MacExpr::new(quote_expr!(cx, ::phf::OrderedMap {
        key: $key,
        disps: &$disps,
        idxs: &$idxs,
        entries: &$entries,
    }))
}

pub fn create_ordered_set(cx: &mut ExtCtxt, sp: Span, entries: Vec<Entry>, state: HashState)
                          -> Box<MacResult+'static> {
    let map = create_ordered_map(cx, sp, entries, state).make_expr().unwrap();
    MacExpr::new(quote_expr!(cx, ::phf::OrderedSet { map: $map }))
}
<|endoftext|>


<commit_before>use std::cmp::PartialOrd;
use num::{Num, Signed, Zero, One, abs};
use {Anchor, Shape2D};

#[derive(Copy, Clone, PartialEq, PartialOrd, Hash, Default, Debug, Serialize, Deserialize)]
pub struct Rect<T: Copy> {
    /// Top left (closest to the origin) corner of the rectange.
    pub top: [T; 2],
    /// Size of the rectangle, all elements are assumed to always be positive.
    pub size: [T; 2],
}

impl<T> Rect<T> where T: Num + PartialOrd + Signed + Copy
{
    /// Create a new rectangle from two corner points.
    pub fn new<V: Into<[T; 2]>>(p1: V, p2: V) -> Rect<T> {
        let (p1, p2): ([T; 2], [T; 2]) = (p1.into(), p2.into());

        Rect {
            top: [*min(&p1[0], &p2[0]), *min(&p1[1], &p2[1])],
            size: [abs(p2[0] - p1[0]), abs(p2[1] - p1[1])],
        }
    }

    pub fn point(&self, point: Anchor) -> [T; 2] {
        let one: T = One::one();
        let two = one + one;
        match point {
            Anchor::TopLeft => self.top,
            Anchor::TopRight => [self.top[0] + self.size[0], self.top[1]],
            Anchor::BottomLeft => [self.top[0], self.top[1] + self.size[1]],
            Anchor::BottomRight => {
                [self.top[0] + self.size[0], self.top[1] + self.size[1]]
            }
            Anchor::Top => [self.top[0] + self.size[0] / two, self.top[1]],
            Anchor::Left => [self.top[0], self.top[1] + self.size[1] / two],
            Anchor::Right => {
                [self.top[0] + self.size[0], self.top[1] + self.size[1] / two]
            }
            Anchor::Bottom => {
                [self.top[0] + self.size[0] / two, self.top[1] + self.size[1]]
            }
            Anchor::Center => {
                [self.top[0] + self.size[0] / two,
                 self.top[1] + self.size[1] / two]
            }
        }
    }

    /// Iterate tiles of the given size inside the rectangle.
    ///
    /// The tiles are ordered from left to right, then from top to bottom. The
    /// first tile snaps to the top left corner of the rectangle, and parts
    /// along the right and bottom edges of the rectangle are left uncovered
    /// if a whole tile will not fit in them.
    ///
    /// ```
    /// use calx_layout::Rect;
    ///
    /// let x = Rect::new([0, 0], [8, 16]);
    /// // To iterate integer points in the rectangle, just use [1, 1] tiles.
    /// assert_eq!(x.tiles([1, 1]).map(|x| x.top).next(), Some([0, 0]));
    /// assert_eq!(x.tiles([1, 1]).count(), 8 * 16);
    ///
    /// let y = Rect::new([0.0, 0.0], [3.141, 2.718]);
    /// assert_eq!(y.tiles([1.0, 1.0]).count(), 3 * 2);
    /// ```
    pub fn tiles<'a, V: Into<[T; 2]>>(&'a self, dim: V) -> TileIter<'a, T> {
        TileIter::new(self, dim.into())
    }

    pub fn area(&self) -> T {
        self.size[0] * self.size[1]
    }

    /// Produce the smallest new rectangle that contains both input
    /// rectangles.
    pub fn merge(&self, other: &Rect<T>) -> Rect<T> {
        Rect::new([*min(&self.top[0], &other.top[0]),
                   *min(&self.top[1], &other.top[1])],
                  [*max(&(self.top[0] + self.size[0]),
                        &(other.top[0] + other.size[0])),
                   *max(&(self.top[1] + self.size[1]),
                        &(other.top[1] + other.size[1]))])

    }
}

impl<T: Copy + Num + PartialOrd> Shape2D<T> for Rect<T> {
    fn bounding_box(&self) -> Rect<T> {
        *self
    }

    fn contains<V: Into<[T; 2]>>(&self, p: V) -> bool {
        let p = p.into();
        p[0] >= self.top[0] && p[1] >= self.top[1] &&
        p[0] < self.top[0] + self.size[0] &&
        p[1] < self.top[1] + self.size[1]
    }
}

/// Iterator for packed left-to-right top-to-bottom subrectangles
pub struct TileIter<'a, T: 'a + Copy> {
    base: &'a Rect<T>,
    dim: [T; 2],
    x: T,
    y: T,
}

impl<'a, T: Num + PartialOrd + Copy + 'a> TileIter<'a, T> {
    fn new(base: &'a Rect<T>, dim: [T; 2]) -> TileIter<T> {
        assert!(dim[0] > Zero::zero() && dim[1] > Zero::zero());
        TileIter {
            base: base,
            dim: dim,
            x: Zero::zero(),
            y: Zero::zero(),
        }
    }
}

impl<'a, T: Num + PartialOrd + Copy> Iterator for TileIter<'a, T> {
    type Item = Rect<T>;

    fn next(&mut self) -> Option<Rect<T>> {
        if self.dim[0] > self.base.size[0] {
            return None;
        }

        if (self.x + One::one()) * self.dim[0] > self.base.size[0] {
            self.y = self.y + One::one();
            self.x = Zero::zero();
        }

        if (self.y + One::one()) * self.dim[1] > self.base.size[1] {
            return None;
        }

        let ret = Rect {
            top: [self.x * self.dim[0], self.y * self.dim[1]],
            size: self.dim,
        };
        self.x = self.x + One::one();
        Some(ret)
    }
}

fn min<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
    if a.lt(b) {
        a
    } else {
        b
    }
}

fn max<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
    if a.gt(b) {
        a
    } else {
        b
    }
}
<commit_msg>Calx-layout tweaks<commit_after>use std::cmp::PartialOrd;
use num::{Num, Signed, Zero, One, abs};
use {Anchor, Shape2D};

#[derive(Copy, Clone, PartialEq, PartialOrd, Hash, Default, Debug, Serialize, Deserialize)]
pub struct Rect<T: Copy> {
    /// Top left (closest to the origin) corner of the rectange.
    pub top: [T; 2],
    /// Size of the rectangle, all elements are assumed to always be positive.
    pub size: [T; 2],
}

impl<T> Rect<T> where T: Num + PartialOrd + Signed + Copy
{
    /// Create a new rectangle from two corner points.
    pub fn new<V: Into<[T; 2]>>(p1: V, p2: V) -> Rect<T> {
        let (p1, p2): ([T; 2], [T; 2]) = (p1.into(), p2.into());

        Rect {
            top: [*min(&p1[0], &p2[0]), *min(&p1[1], &p2[1])],
            size: [abs(p2[0] - p1[0]), abs(p2[1] - p1[1])],
        }
    }

    pub fn point(&self, point: Anchor) -> [T; 2] {
        let one: T = One::one();
        let two = one + one;
        match point {
            Anchor::TopLeft => self.top,
            Anchor::TopRight => [self.top[0] + self.size[0], self.top[1]],
            Anchor::BottomLeft => [self.top[0], self.top[1] + self.size[1]],
            Anchor::BottomRight => {
                [self.top[0] + self.size[0], self.top[1] + self.size[1]]
            }
            Anchor::Top => [self.top[0] + self.size[0] / two, self.top[1]],
            Anchor::Left => [self.top[0], self.top[1] + self.size[1] / two],
            Anchor::Right => {
                [self.top[0] + self.size[0], self.top[1] + self.size[1] / two]
            }
            Anchor::Bottom => {
                [self.top[0] + self.size[0] / two, self.top[1] + self.size[1]]
            }
            Anchor::Center => {
                [self.top[0] + self.size[0] / two,
                 self.top[1] + self.size[1] / two]
            }
        }
    }

    /// Iterate tiles of the given size inside the rectangle.
    ///
    /// The tiles are ordered from left to right, then from top to bottom. The
    /// first tile snaps to the top left corner of the rectangle, and parts
    /// along the right and bottom edges of the rectangle are left uncovered
    /// if a whole tile will not fit in them.
    ///
    /// ```
    /// use calx_layout::Rect;
    ///
    /// let x = Rect::new([0, 0], [8, 16]);
    /// // To iterate integer points in the rectangle, just use [1, 1] tiles.
    /// assert_eq!(x.tiles([1, 1]).map(|t| t.top).next(), Some([0, 0]));
    /// assert_eq!(x.tiles([1, 1]).count(), 8 * 16);
    ///
    /// let y = Rect::new([0.0, 0.0], [3.141, 2.718]);
    /// assert_eq!(y.tiles([1.0, 1.0]).count(), 3 * 2);
    ///
    /// let z = Rect::new([3, 6], [8, 16]);
    /// assert_eq!(z.tiles([1, 1]).map(|t| t.top).next(), Some([3, 6]));
    /// ```
    pub fn tiles<'a, V: Into<[T; 2]>>(&'a self, dim: V) -> RectIter<'a, T> {
        RectIter::new(self, dim.into())
    }

    pub fn area(&self) -> T {
        self.size[0] * self.size[1]
    }

    /// Produce the smallest new rectangle that contains both input
    /// rectangles.
    pub fn merge(&self, other: &Rect<T>) -> Rect<T> {
        Rect::new([*min(&self.top[0], &other.top[0]),
                   *min(&self.top[1], &other.top[1])],
                  [*max(&(self.top[0] + self.size[0]),
                        &(other.top[0] + other.size[0])),
                   *max(&(self.top[1] + self.size[1]),
                        &(other.top[1] + other.size[1]))])

    }

    /// Return whether this rect completely contains another rect.
    pub fn contains_rect(&self, other: &Rect<T>) -> bool {
        let p2 = self.point(Anchor::BottomRight);
        let q2 = other.point(Anchor::BottomRight);
        self.contains(other.point(Anchor::TopLeft)) && q2[0] <= p2[0] &&
        q2[1] <= p2[1]
    }
}

impl<T: Copy + Num + PartialOrd> Shape2D<T> for Rect<T> {
    fn bounding_box(&self) -> Rect<T> {
        *self
    }

    fn contains<V: Into<[T; 2]>>(&self, p: V) -> bool {
        let p = p.into();
        p[0] >= self.top[0] && p[1] >= self.top[1] &&
        p[0] < self.top[0] + self.size[0] &&
        p[1] < self.top[1] + self.size[1]
    }
}

/// Iterator for packed left-to-right top-to-bottom subrectangles
pub struct RectIter<'a, T: 'a + Copy> {
    base: &'a Rect<T>,
    dim: [T; 2],
    x: T,
    y: T,
}

impl<'a, T: Num + PartialOrd + Copy + 'a> RectIter<'a, T> {
    fn new(base: &'a Rect<T>, dim: [T; 2]) -> RectIter<T> {
        assert!(dim[0] > Zero::zero() && dim[1] > Zero::zero());
        RectIter {
            base: base,
            dim: dim,
            x: Zero::zero(),
            y: Zero::zero(),
        }
    }
}

impl<'a, T: Num + PartialOrd + Copy> Iterator for RectIter<'a, T> {
    type Item = Rect<T>;

    fn next(&mut self) -> Option<Rect<T>> {
        if self.dim[0] > self.base.size[0] {
            return None;
        }

        if (self.x + One::one()) * self.dim[0] > self.base.size[0] {
            self.y = self.y + One::one();
            self.x = Zero::zero();
        }

        if (self.y + One::one()) * self.dim[1] > self.base.size[1] {
            return None;
        }

        let ret = Rect {
            top: [self.base.top[0] + self.x * self.dim[0], self.base.top[1] + self.y * self.dim[1]],
            size: self.dim,
        };
        self.x = self.x + One::one();
        Some(ret)
    }
}

fn min<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
    if a.lt(b) {
        a
    } else {
        b
    }
}

fn max<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
    if a.gt(b) {
        a
    } else {
        b
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use chrono::{NaiveDate, ParseError};
use reqwest;
use reqwest::Response;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Parse the date, trying out each format
pub fn parse_date(input: &str) -> Result<NaiveDate, ParseError> {
    let parse_attempt: Vec<Result<NaiveDate,ParseError>> = FMTS.iter().map(|&fmt| NaiveDate::parse_from_str(input, fmt)).collect::<Vec<Result<NaiveDate, ParseError>>>();
    try_filter_dates(parse_attempt)
}

// Attempt to find a successful parse, if there are none, return the first error encountered
fn try_filter_dates(vec: Vec<Result<NaiveDate, ParseError>>) -> Result<NaiveDate, ParseError> {
    let orig_vec: Vec<Result<NaiveDate, ParseError>> = vec.clone();
    let filtered: Vec<Result<NaiveDate, ParseError>> = vec.into_iter().filter(|&item| item.is_ok()).collect();
    match filtered.first() {
        Some(v) => v.to_owned(),
        None => orig_vec.first().unwrap().to_owned(),
    }
}

// Extract date from a URL and pass it to parse_date
fn extract_from_url(url: &str) -> Result<NaiveDate, ParseError> {
    // Use lazy_static to ensure we only compile the regex once
    lazy_static! {
       // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
       static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})?").unwrap();
    }
    match RE.find(url) {
        Some(v) => parse_date(v.as_str()),
        None => parse_date(""),
    }
}

fn extract_from_ldjson<'a>(html: &'a Document) -> Result<NaiveDate, ParseError> {
    let mut json_date = String::new();
    let json = html.find(Attr("type", "application/ld+json")).next().unwrap().text();

    let decoded_json = Json::from_str(json.as_str()).unwrap();

    if let Some(date_published) = decoded_json.search("datePublished") {
        json_date = date_published.as_string().unwrap_or("").to_string();
    } else if let Some(date_created) = decoded_json.search("dateCreated") {
        json_date = date_created.as_string().unwrap_or("").to_string();
    }

    parse_date(json_date.as_str())
}

// Attempt to extract the date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Result<NaiveDate, ParseError> {
    let mut meta_date = String::new();

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                                                break 'outer; },
            _ => meta_date = String::new(),
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                 break 'outer; },
            _ => meta_date = String::new(),
        }

        match http_equiv.as_ref() {
            "date" =>  { meta_date = meta.attr("content").unwrap().trim().to_string();
                         break 'outer; },
            _ => meta_date = String::new(),
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                         break 'outer; },
            "og:image"                              => { let url = meta.attr("content").unwrap().trim();
                                                         let possible_date = extract_from_url(url);
                                                         if possible_date.is_ok() {
                                                           return possible_date
                                                         } },
            _ => meta_date = String::new(),
        }

    }
    parse_date(meta_date.as_str())
}
// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use chrono::{NaiveDate};
    use reqwest;
    use std::string::String;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(parse_date("/2015/11/30/"), Ok(NaiveDate::from_ymd(2015,11,30)));
        assert_eq!(parse_date("/2015/30/11/"), Ok(NaiveDate::from_ymd(2015,11,30)));
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(extract_from_url(link), parse_date("/2015/11/28/"));

        let link = "";
        assert_eq!(extract_from_url(link), parse_date(""));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Ok(NaiveDate::from_ymd(2015,11,30)), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Ok(NaiveDate::from_ymd(2015,12,01)), extract_from_ldjson(&document));
        // println!("{:?}", extract_from_ldjson(&document));
        // println!("{:?}", parse_date("2015-12-01T07:50:48Z"));
        // println!("{:?}", parse_date(extract_from_ldjson(&document).trim()));
    }
}
<commit_msg>Rework date parsing to handle new types<commit_after>use regex::Regex;
use chrono::{NaiveDate, ParseError};
use reqwest;
use reqwest::Response;
use std::io::Read;
use select::document::Document;
use select::predicate::{Name, Attr};
use rustc_serialize::json::Json;
use errors::*;

// Some formats borrowed from https://github.com/amir/article-date-extractor
static FMTS: &'static [&str] = &["/%Y/%m/%d/", "/%Y/%d/%m/", "%Y-%m-%d", "%B %e, %Y", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%SZ", "%B %k, %Y, %H:%M %p", "%Y-%m-%d %H:%M:%S.000000"];

// Use lazy_static to ensure we only compile the regex once
lazy_static! {
    // Regex by Newspaper3k  - https://github.com/codelucas/newspaper/blob/master/newspaper/urls.py
    static ref RE: Regex = Regex::new(r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})?").unwrap();
}

// Parse the date, trying out each format
fn parse_date(input: &str) -> Result<NaiveDate> {
    let mut result: Result<NaiveDate> = Err("None of the formats matched the date".into());

    'outer: for fmt in FMTS {
        if let Ok(v) = NaiveDate::parse_from_str(input, fmt) {
            { result = Ok(v);
              break 'outer; }
        }
    }

    result
}

// Extract date from a URL
fn extract_from_url(url: &str) -> Option<String> {
    let mut date: Option<String> = None;

    if let Some(val) = RE.find(url) {
        date = Some(val.as_str().to_string());
    }

    date
}

fn extract_from_ldjson<'a>(html: &'a Document) -> Result<NaiveDate, ParseError> {
    let mut json_date = String::new();
    let json = html.find(Attr("type", "application/ld+json")).next().unwrap().text();

    let decoded_json = Json::from_str(json.as_str()).unwrap();

    if let Some(date_published) = decoded_json.search("datePublished") {
        json_date = date_published.as_string().unwrap_or("").to_string();
    } else if let Some(date_created) = decoded_json.search("dateCreated") {
        json_date = date_created.as_string().unwrap_or("").to_string();
    }

    parse_date(json_date.as_str())
}

// Attempt to extract the date from meta tags
fn extract_from_meta<'a>(html: &'a Document) -> Result<NaiveDate, ParseError> {
    let mut meta_date = String::new();

    'outer: for meta in html.find(Name("meta")) {
        let meta_name     = meta.attr("name").unwrap_or("").to_lowercase();
        let item_prop     = meta.attr("itemprop").unwrap_or("").to_lowercase();
        let http_equiv    = meta.attr("http-equiv").unwrap_or("").to_lowercase();
        let meta_property = meta.attr("property").unwrap_or("").to_lowercase();

        match meta_name.as_ref() {
            "pubdate"               | "publishdate"                  | "timestamp"       |
            "dc.date.issued"        | "date"                         | "sailthru.date"   |
            "article.published"     | "published-date"               | "article.created" |
            "article_date_original" | "cxenseparse:recs:publishtime" | "date_published"  => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                                                break 'outer; },
            _ => meta_date = String::new(),
        }

        match item_prop.as_ref() {
            "datepublished" | "datecreated" => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                 break 'outer; },
            _ => meta_date = String::new(),
        }

        match http_equiv.as_ref() {
            "date" =>  { meta_date = meta.attr("content").unwrap().trim().to_string();
                         break 'outer; },
            _ => meta_date = String::new(),
        }

        match meta_property.as_ref() {
            "article:published_time" | "bt:pubdate" => { meta_date = meta.attr("content").unwrap().trim().to_string();
                                                         break 'outer; },
            "og:image"                              => { let url = meta.attr("content").unwrap().trim();
                                                         let possible_date = extract_from_url(url);
                                                         if possible_date.is_ok() {
                                                           return possible_date
                                                         } },
            _ => meta_date = String::new(),
        }

    }
    parse_date(meta_date.as_str())
}
// Unit tests
#[cfg(test)]
mod test {
    use super::extract_from_url;
    use super::parse_date;
    use super::extract_from_meta;
    use super::extract_from_ldjson;
    use chrono::{NaiveDate};
    use reqwest;
    use std::string::String;
    use std::io::Read;
    use select::document::Document;

    #[test]
    fn parsing_date() {
        assert_eq!(parse_date("/2015/11/30/"), Ok(NaiveDate::from_ymd(2015,11,30)));
        assert_eq!(parse_date("/2015/30/11/"), Ok(NaiveDate::from_ymd(2015,11,30)));
    }

    #[test]
    fn extracting_from_url() {
        let link = "http://edition.cnn.com/2015/11/28/opinions/sutter-cop21-paris-preview-two-degrees/index.html";
        assert_eq!(extract_from_url(link), parse_date("/2015/11/28/"));

        let link = "";
        assert_eq!(extract_from_url(link), parse_date(""));
    }

    #[test]
    fn extracting_from_meta() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Ok(NaiveDate::from_ymd(2015,11,30)), extract_from_meta(&document));
    }

    #[test]
    fn extracting_from_ldjson() {
        let mut response = reqwest::get("https://techcrunch.com/2015/11/30/atlassian-share-price/").unwrap();
        let mut body = String::new();
        response.read_to_string(&mut body).unwrap();
        let document = Document::from(body.as_str());

        assert_eq!(Ok(NaiveDate::from_ymd(2015,12,01)), extract_from_ldjson(&document));
        // println!("{:?}", extract_from_ldjson(&document));
        // println!("{:?}", parse_date("2015-12-01T07:50:48Z"));
        // println!("{:?}", parse_date(extract_from_ldjson(&document).trim()));
    }
}
<|endoftext|>


<commit_before>macro_rules! make_array {
    ($n: expr, $constructor:expr) => {
        unsafe {
            let mut items: [_; $n] = mem::zeroed();
            for place in items.iter_mut() {
                std::ptr::write(place, $constructor);
            }
            items
        }
    };
}

macro_rules! impl_slice_ops {
    ($t: ty, $et: ty, $n: expr) => {
        impl Slice<$et> for $t {
            fn as_slice(&mut self) -> &mut [$et] {
                self
            }
            fn init() -> Self {
                make_array!($n, Self::item_default())
            }
            fn slice_len() -> usize {
                $n
            }
        }
    };
}
<commit_msg>make array by zeroed for fast allocation<commit_after>macro_rules! make_array {
    ($n: expr) => {
        unsafe {
            mem::zeroed()
        }
    };
}

macro_rules! impl_slice_ops {
    ($t: ty, $et: ty, $n: expr) => {
        impl Slice<$et> for $t {
            fn as_slice(&mut self) -> &mut [$et] {
                self
            }
            fn init() -> Self {
                make_array!($n)
            }
            fn slice_len() -> usize {
                $n
            }
        }
    };
}
<|endoftext|>