<commit_before>#![cfg(target_os = "windows")]

pub mod ffi;

use std::ptr;
use std::mem;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;

fn to_utf16_os(s: &str) -> Vec<u16> {
   let mut v: Vec<_> = OsStr::new(s).encode_wide().collect();
   v.push(0);
   v
}

#[allow(unused_variables)]
unsafe extern "system" fn wnd_proc(
   hwnd: ffi::HWND,
   msg: ffi::c_uint,
   wparam: ffi::WPARAM,
   lparam: ffi::LPARAM
) -> ffi::LRESULT {

   let window_ptr = ffi::GetWindowLongPtrW(hwnd, ffi::GWLP_USERDATA) as *mut Window;

   match msg {
      ffi::WM_NCCREATE => {
         let create_struct: *const ffi::CREATESTRUCTW = lparam as *const _;
         ffi::SetWindowLongPtrW(hwnd, ffi::GWLP_USERDATA, (*create_struct).lpCreateParams as ffi::LONG_PTR);
      },
      ffi::WM_CLOSE => {
         ffi::PostQuitMessage(0);
         return 0;
      },
      ffi::WM_CREATE => {
         return 0;
      },
      _ => {
         return ffi::DefWindowProcW(hwnd, msg, wparam, lparam);
      }
   }

   ffi::DefWindowProcW(hwnd, msg, wparam, lparam)
}

pub struct Window {
   pub hwnd: ffi::HWND
}

impl Window {
   pub fn new(width: u32, height: u32, title: &str, class_name: &str) -> Self {
      Self::register_class(class_name);

      let mut window: Window = Window {
         hwnd: ptr::null_mut(),
      };

      let window_ptr = &mut window as *mut Window as *mut ffi::c_void;

      let hwnd = unsafe {
         ffi::CreateWindowExW(
            ffi::WS_EX_APPWINDOW | ffi::WS_EX_WINDOWEDGE,
            to_utf16_os(class_name).as_ptr(),
            to_utf16_os(title).as_ptr(),
            ffi::WS_OVERLAPPEDWINDOW | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN,
            ffi::CW_USEDEFAULT, ffi::CW_USEDEFAULT,
            width as ffi::c_int, height as ffi::c_int,
            ptr::null_mut(),
            ptr::null_mut(),
            ffi::GetModuleHandleW(ptr::null()),
            window_ptr
         )
      };

      window.hwnd = hwnd;

      window
   }

   #[inline]
   pub fn register_class(class_name: &str) {
      unsafe {
         let wnd_class = ffi::WNDCLASSEXW {
            cbSize: mem::size_of::<ffi::WNDCLASSEXW>() as ffi::c_uint,
            style: ffi::CS_HREDRAW | ffi::CS_VREDRAW | ffi::CS_OWNDC,
            lpfnWndProc: Some(wnd_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: ffi::GetModuleHandleW(ptr::null()),
            hIcon: ptr::null_mut(),
            hCursor: ptr::null_mut(),
            hbrBackground: ptr::null_mut(),
            lpszMenuName: ptr::null(),
            lpszClassName: to_utf16_os(class_name).as_ptr(),
            hIconSm: ptr::null_mut(),
         };

         ffi::RegisterClassExW(&wnd_class);
      }
   }

   #[inline]
   pub fn device_context(&self) -> DeviceContext {
      let hdc = unsafe {
         ffi::GetDC(self.hwnd)
      };

      DeviceContext {
         hdc: hdc
      }
   }

   #[inline]
   pub fn show_normal(&self) {
      unsafe {
         ffi::ShowWindow(self.hwnd, ffi::SW_SHOWNORMAL)
      };
   }

   #[inline]
   pub fn position(&self, x: u32, y: u32) {
      unsafe {
         ffi::SetWindowPos(
            self.hwnd,
            ptr::null_mut(),
            x as ffi::c_int, y as ffi::c_int,
            0, 0,
            ffi::SWP_NOZORDER | ffi::SWP_NOSIZE
         )
      };
   }
}

pub struct Message {
   msg: ffi::MSG
}

impl Message {
   pub fn get() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   #[inline]
   pub fn peek() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, ffi::PM_REMOVE) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   #[inline]
   pub fn translate(&self) {
      unsafe {
         ffi::TranslateMessage(&self.msg)
      };
   }

   #[inline]
   pub fn dispatch(&self) {
      unsafe {
         ffi::DispatchMessageW(&self.msg)
      };
   }

   #[inline]
   pub fn is_quit(&self) -> bool {
      return self.msg.message == ffi::WM_QUIT
   }
}

pub struct DeviceContext {
   pub hdc: ffi::HDC
}

pub struct DeviceMode {
   pub device_mode: ffi::DEVMODEW
}

impl DeviceMode {
   pub fn enumerate() -> Self {
      let mut device_mode = ffi::DEVMODEW::default();
      device_mode.dmSize = mem::size_of::<ffi::DEVMODEW>() as ffi::WORD;

      unsafe {
         ffi::EnumDisplaySettingsW(
            ptr::null(),
            ffi::ENUM_CURRENT_SETTINGS,
            &mut device_mode
         )
      };

      DeviceMode {
         device_mode: device_mode,
      }
   }

   #[inline]
   pub fn screen_size(&self) -> (u32, u32) {
      (self.device_mode.dmPelsWidth, self.device_mode.dmPelsHeight)
   }
}
<commit_msg>Not passing the win32 window pointer to the window proc<commit_after>#![cfg(target_os = "windows")]

pub mod ffi;

use std::ptr;
use std::mem;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;

fn to_utf16_os(s: &str) -> Vec<u16> {
   let mut v: Vec<_> = OsStr::new(s).encode_wide().collect();
   v.push(0);
   v
}

#[allow(unused_variables)]
unsafe extern "system" fn wnd_proc(
   hwnd: ffi::HWND,
   msg: ffi::c_uint,
   wparam: ffi::WPARAM,
   lparam: ffi::LPARAM
) -> ffi::LRESULT {
   match msg {
      ffi::WM_CLOSE => {
         ffi::PostQuitMessage(0);
         return 0;
      },
      ffi::WM_CREATE => {
         return 0;
      },
      _ => {}
   }

   ffi::DefWindowProcW(hwnd, msg, wparam, lparam)
}

pub struct Window {
   pub hwnd: ffi::HWND
}

impl Window {
   pub fn new(width: u32, height: u32, title: &str, class_name: &str) -> Self {
      Self::register_class(class_name);

      let hwnd = unsafe {
         ffi::CreateWindowExW(
            ffi::WS_EX_APPWINDOW | ffi::WS_EX_WINDOWEDGE,
            to_utf16_os(class_name).as_ptr(),
            to_utf16_os(title).as_ptr(),
            ffi::WS_OVERLAPPEDWINDOW | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN,
            ffi::CW_USEDEFAULT, ffi::CW_USEDEFAULT,
            width as ffi::c_int, height as ffi::c_int,
            ptr::null_mut(),
            ptr::null_mut(),
            ffi::GetModuleHandleW(ptr::null()),
            ptr::null_mut()
         )
      };

      Window {
         hwnd: hwnd,
      }
   }

   #[inline]
   pub fn register_class(class_name: &str) {
      unsafe {
         let wnd_class = ffi::WNDCLASSEXW {
            cbSize: mem::size_of::<ffi::WNDCLASSEXW>() as ffi::c_uint,
            style: ffi::CS_HREDRAW | ffi::CS_VREDRAW | ffi::CS_OWNDC,
            lpfnWndProc: Some(wnd_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: ffi::GetModuleHandleW(ptr::null()),
            hIcon: ptr::null_mut(),
            hCursor: ptr::null_mut(),
            hbrBackground: ptr::null_mut(),
            lpszMenuName: ptr::null(),
            lpszClassName: to_utf16_os(class_name).as_ptr(),
            hIconSm: ptr::null_mut(),
         };

         ffi::RegisterClassExW(&wnd_class);
      }
   }

   #[inline]
   pub fn device_context(&self) -> DeviceContext {
      let hdc = unsafe {
         ffi::GetDC(self.hwnd)
      };

      DeviceContext {
         hdc: hdc
      }
   }

   #[inline]
   pub fn show_normal(&self) {
      unsafe {
         ffi::ShowWindow(self.hwnd, ffi::SW_SHOWNORMAL)
      };
   }

   #[inline]
   pub fn position(&self, x: u32, y: u32) {
      unsafe {
         ffi::SetWindowPos(
            self.hwnd,
            ptr::null_mut(),
            x as ffi::c_int, y as ffi::c_int,
            0, 0,
            ffi::SWP_NOZORDER | ffi::SWP_NOSIZE
         )
      };
   }
}

pub struct Message {
   msg: ffi::MSG
}

impl Message {
   pub fn get() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   #[inline]
   pub fn peek() -> Option<Self> {
      let mut msg = unsafe { mem::uninitialized() };

      match unsafe { ffi::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, ffi::PM_REMOVE) } {
         0 => None,
         _ => Some(Message {
            msg: msg
         })
      }
   }

   #[inline]
   pub fn translate(&self) {
      unsafe {
         ffi::TranslateMessage(&self.msg)
      };
   }

   #[inline]
   pub fn dispatch(&self) {
      unsafe {
         ffi::DispatchMessageW(&self.msg)
      };
   }

   #[inline]
   pub fn is_quit(&self) -> bool {
      return self.msg.message == ffi::WM_QUIT
   }
}

pub struct DeviceContext {
   pub hdc: ffi::HDC
}

pub struct DeviceMode {
   pub device_mode: ffi::DEVMODEW
}

impl DeviceMode {
   pub fn enumerate() -> Self {
      let mut device_mode = ffi::DEVMODEW::default();
      device_mode.dmSize = mem::size_of::<ffi::DEVMODEW>() as ffi::WORD;

      unsafe {
         ffi::EnumDisplaySettingsW(
            ptr::null(),
            ffi::ENUM_CURRENT_SETTINGS,
            &mut device_mode
         )
      };

      DeviceMode {
         device_mode: device_mode,
      }
   }

   #[inline]
   pub fn screen_size(&self) -> (u32, u32) {
      (self.device_mode.dmPelsWidth, self.device_mode.dmPelsHeight)
   }
}
<|endoftext|>


<commit_before>//! Methods for converting shapes into triangles.

use std;
use {
    Color,
    Image,
    Line,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};
use vecmath::{
    multiply,
    orient,
    translate,
};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * std::f64::consts::PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: &Matrix2d,
    line: &Line,
    round_border_radius: &f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = *round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, &translate(x1, y1));
    let m = multiply(&m, &orient(dx, dy));
    let n = resolution_cap * 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(&m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_cap => {
                let angle = (j - resolution_cap) as f64 / (resolution_cap - 1) as f64 * std::f64::consts::PI
                    + std::f64::consts::PI;
                let angle = angle + std::f64::consts::FRAC_PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (resolution_cap - 1) as f64 * std::f64::consts::PI;
                let angle = angle + std::f64::consts::FRAC_PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
    /*
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_border[0];
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
    */
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: &Color) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<commit_msg>Put 'use' statements for PI constants in `triangulation`<commit_after>//! Methods for converting shapes into triangles.

use std::f64::consts::{
    PI,
    PI_2,
    FRAC_PI_2,
};
use {
    Color,
    Image,
    Line,
    Matrix2d,
    Rectangle,
};
use interpolation::{lerp};
use vecmath::{
    multiply,
    orient,
    translate,
};

#[inline(always)]
fn tx(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[0] * x + m[1] * y + m[2]) as f32
}

#[inline(always)]
fn ty(m: &Matrix2d, x: f64, y: f64) -> f32 {
    (m[3] * x + m[4] * y + m[5]) as f32
}

/// Streams tweened polygons using linear interpolation.
#[inline(always)]
pub fn with_lerp_polygons_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygons: &[&[f64]],
    tween_factor: f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let poly_len = polygons.len() as f64;
    // Map to interval between 0 and 1.
    let tw = tween_factor % 1.0;
    // Map negative values to positive.
    let tw = if tw < 0.0 { tw + 1.0 } else { tw };
    // Map to frame.
    let tw = tw * poly_len;
    // Get the current frame.
    let frame = tw as uint;
    // Get the next frame.
    let next_frame = (frame + 1) % polygons.len();
    let p0 = polygons[frame];
    let p1 = polygons[next_frame];
    // Get factor between frames.
    let tw = tw - frame as f64;
    let n = polygons[0].len();
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 2;
        let (x0, y0) = (p0[j], p0[j+1]);
        let (x1, y1) = (p1[j], p1[j+1]);
        Some([lerp(&x0, &x1, &tw), lerp(&y0, &y1, &tw)])
    }, color, f);
}

/// Streams an ellipse specified by a resolution.
#[inline(always)]
pub fn with_ellipse_tri_list_xy_f32_rgba_f32(
    resolution: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (cw, ch) = (0.5 * w, 0.5 * h);
    let (cx, cy) = (x + cw, y + ch);
    let n = resolution;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let angle = i as f64 / n as f64 * PI_2;
        i += 1;
        Some([cx + angle.cos() * cw, cy + angle.sin() * ch])
    }, color, f);
}

/// Streams a round border line.
#[inline(always)]
pub fn with_round_border_line_tri_list_xy_f32_rgba_f32(
    resolution_cap: uint,
    m: &Matrix2d,
    line: &Line,
    round_border_radius: &f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let radius = *round_border_radius;
    let (x1, y1, x2, y2) = (line[0], line[1], line[2], line[3]);
    let (dx, dy) = (x2 - x1, y2 - y1);
    let w = (dx * dx + dy * dy).sqrt();
    let m = multiply(m, &translate(x1, y1));
    let m = multiply(&m, &orient(dx, dy));
    let n = resolution_cap * 2;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(&m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_cap => {
                let angle = (j - resolution_cap) as f64 / (resolution_cap - 1) as f64 * PI + PI;
                let angle = angle + FRAC_PI_2;
                Some([w + angle.cos() * radius, angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (resolution_cap - 1) as f64 * PI;
                let angle = angle + FRAC_PI_2;
                Some([angle.cos() * radius, angle.sin() * radius])
            },
        }
    }, color, f);
    /*
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = round_border[0];
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * std::f64::consts::PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
    */
}

/// Streams a round rectangle.
#[inline(always)]
pub fn with_round_rectangle_tri_list_xy_f32_rgba_f32(
    resolution_corner: uint,
    m: &Matrix2d,
    rect: &Rectangle,
    round_radius: &f64,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let radius = *round_radius;
    let n = resolution_corner * 4 + 4;
    let mut i = 0u;
    stream_polygon_tri_list_xy_f32_rgba_f32(m, || {
        if i >= n { return None; }

        let j = i;
        i += 1;
        match j {
            j if j >= resolution_corner * 3 => {
                let angle = j as f64 / (n - 3) as f64 * PI_2;
                let (cx, cy) = (x + w - radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 2 => {
                let angle = j as f64 / (n - 2) as f64 * PI_2;
                let (cx, cy) = (x + radius, y + radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j if j >= resolution_corner * 1 => {
                let angle = j as f64 / (n - 1) as f64 * PI_2;
                let (cx, cy) = (x + radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
            j => {
                let angle = j as f64 / (n - 0) as f64 * PI_2;
                let (cx, cy) = (x + w - radius, y + h - radius);
                Some([cx + angle.cos() * radius, cy + angle.sin() * radius])
            },
        }
    }, color, f);
}

/// Streams a polygon into tri list with color per vertex.
/// Uses buffers that fit inside L1 cache.
pub fn stream_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: || -> Option<[f64, ..2]>,
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let mut vertices: [f32, ..740] = [0.0, ..740];
    let mut colors: [f32, ..1480] = [0.0, ..1480];
    // Get the first point which will be used a lot.
    let fp = match polygon() { None => return, Some(val) => val };
    let (fx, fy) = (tx(m, fp[0], fp[1]), ty(m, fp[0], fp[1]));
    let gp = match polygon() { None => return, Some(val) => val };
    let (gx, gy) = (tx(m, gp[0], gp[1]), ty(m, gp[0], gp[1]));
    let mut gx = gx;
    let mut gy = gy;
    let mut i = 0;
    'read_vertices: loop {
        let ind_out = i * 2 * 3;
        vertices[ind_out + 0] = fx;
        vertices[ind_out + 1] = fy;
        let ind_out = i * 4 * 3;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];

        // Copy vertex.
        let ind_out = i * 2 * 3 + 2;
        let p =
            match polygon() {
                None => break 'read_vertices,
                Some(val) => val,
            };
        let x = tx(m, p[0], p[1]);
        let y = ty(m, p[0], p[1]);

        vertices[ind_out + 0] = gx;
        vertices[ind_out + 1] = gy;
        vertices[ind_out + 2] = x;
        vertices[ind_out + 3] = y;
        gx = x;
        gy = y;
        let ind_out = i * 4 * 3 + 4;
        colors[ind_out + 0] = color[0];
        colors[ind_out + 1] = color[1];
        colors[ind_out + 2] = color[2];
        colors[ind_out + 3] = color[3];
        colors[ind_out + 4] = color[0];
        colors[ind_out + 5] = color[1];
        colors[ind_out + 6] = color[2];
        colors[ind_out + 7] = color[3];

        i += 1;
        // Buffer is full.
        if i * 2 * 3 + 2 == vertices.len() {
            // Send chunk and start over.
            f(vertices.slice(0, i * 2 * 3),
                colors.slice(0, i * 4 * 3));
            i = 0;
        }
    }

    if i > 0 {
        f(vertices.slice(0, i * 2 * 3),
            colors.slice(0, i * 4 * 3));
    }
}

/// Splits polygon into convex segments with one color per vertex.
/// Create a buffer that fits into L1 cache with 1KB overhead.
pub fn with_polygon_tri_list_xy_f32_rgba_f32(
    m: &Matrix2d,
    polygon: &[f64],
    color: &Color,
    f: |vertices: &[f32], colors: &[f32]|) {

    let n = polygon.len();
    let mut i = 0;
    stream_polygon_tri_list_xy_f32_rgba_f32(
        m, || {
            if i >= n { return None; }

            let j = i;
            i += 2;
            Some([polygon[j], polygon[j+1]])
        }, color, f);
}

/// Creates triangle list vertices from rectangle.
pub fn rect_tri_list_xy_f32(m: &Matrix2d, rect: &Rectangle) -> [f32, ..12] {
    let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
    let (x2, y2) = (x + w, y + h);
    [tx(m,x,y), ty(m,x,y), tx(m,x2,y), ty(m,x2,y), tx(m,x,y2), ty(m,x,y2),
     tx(m,x2,y), ty(m,x2,y), tx(m,x2,y2), ty(m,x2,y2), tx(m,x,y2), ty(m,x,y2)]
}

/// Creates triangle list colors from rectangle.
pub fn rect_tri_list_rgba_f32(color: &Color) -> [f32, ..48] {
    let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
    [r, g, b, a, // 0
     r, g, b, a, // 1
     r, g, b, a, // 2
     r, g, b, a, // 3
     r, g, b, a, // 4
     r, g, b, a, // 5
     r, g, b, a, // 6
     r, g, b, a, // 7
     r, g, b, a, // 8
     r, g, b, a, // 9
     r, g, b, a, // 10
     r, g, b, a]
}

/// Creates triangle list texture coords from image.
pub fn rect_tri_list_uv_f32(image: &Image) -> [f32, ..12] {
    let x1 = image.source_rect[0] as f32 / image.texture_width as f32;
    let y1 = image.source_rect[1] as f32 / image.texture_height as f32;
    let x2 = (image.source_rect[0] + image.source_rect[2]) as f32 / image.texture_width as f32;
    let y2 = (image.source_rect[1] + image.source_rect[3]) as f32 / image.texture_height as f32;
    [x1, y1, x2, y1, x1, y2,
     x2, y1, x2, y2, x1, y2]
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs::{read_dir, read_link, remove_file, metadata};
use std::io::{self, stdout, stderr};
use std::path::Path;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use std::os::unix::fs::MetadataExt;

use argparse::{ArgumentParser, PushConst, StoreTrue, StoreOption};
use scan_dir::ScanDir;
use humantime;

use super::setup;
use super::Wrapper;
use container::util::clean_dir;
use file_util::read_visible_entries;
use wrapper::build::get_version_hash;

#[derive(Clone, Copy)]
enum Action {
    Temporary,
    Old,
    Unused,
    Everything,
    Transient,
}

pub fn clean_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut global = false;
    let mut dry_run = false;
    let mut actions = vec!();
    let mut duration = None::<humantime::Duration>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Performs various cleanup tasks
            ");
        ap.refer(&mut actions)
          .add_option(&["--tmp", "--tmp-folders"],
                PushConst(Action::Temporary),
                "Clean temporary containers (failed builds)")
          .add_option(&["--old", "--old-containers"], PushConst(Action::Old), "
                Clean old versions of containers (those which doesn't have a \
                symlink in .vagga)")
          .add_option(&["--unused"], PushConst(Action::Unused), "
                Clean unused containers, or versions thereof. (This is not \
                `--old` for historical reasons, we will probably merge the \
                commands later on)")
          .add_option(&["--transient"], PushConst(Action::Transient),
                "Clean unneeded transient folders (left from containers with
                 `write-mode` set to transient-something). The pid of process
                 is checked for liveness first.")
          .add_option(&["--everything"], PushConst(Action::Everything),
                "Clean whole `.vagga` folder. Useful when deleting a project.
                 With ``--global`` cleans whole storage-dir and cache-dir")
          .required();
        ap.refer(&mut global)
          .add_option(&["--global"], StoreTrue,
                "Apply cleanup command to all containers. Works only \
                if `storage-dir` is configured in settings");
        ap.refer(&mut dry_run)
          .add_option(&["-n", "--dry-run"], StoreTrue,
                "Dry run. Don't delete everything, just print");
        ap.refer(&mut duration)
          .add_option(&["--at-least"], StoreOption, "
            Only in combination with `--unused`. Treat as unused \
            containers that are unused for specified time, rather than \
            the ones not used by current version of config");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(1);
            }
        }
    }
    if global && wrapper.ext_settings.storage_dir.is_none() {
        error!("The --global flag is only meaningful if you configure \
            storage-dir in settings");
        return Ok(2);
    }
    let duration = duration.map(|x| x.into());
    for action in actions.iter() {
        let res = match *action {
            Action::Temporary => clean_temporary(wrapper, global, dry_run),
            Action::Old => clean_old(wrapper, global, dry_run),
            Action::Unused => {
                clean_unused(wrapper, global, duration, dry_run)
            }
            Action::Transient => clean_transient(wrapper, global, dry_run),
            _ => unimplemented!(),
        };
        match res {
            Ok(()) => {}
            Err(err) => {
                error!("Error cleaning up: {}", err);
                return Ok(3);
            }
        }
    }
    return Ok(0);
}

fn clean_dir_wrapper(path: &Path, dry_run: bool) -> Result<(), String> {
    // TODO(tailhook) chroot to dir for removing
    if dry_run {
        println!("Would remove {:?}", path);
    } else {
        debug!("Removing {:?}", path);
        try!(clean_dir(path, true));
    }
    Ok(())
}

fn clean_temporary(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let roots = base.join(".roots");
    for entry in try_msg!(read_dir(&roots),
        "Can't read dir {r:?}: {err}", r=roots)
    {
        let entry = try_msg!(entry, "Can't read dir {r:?}: {err}", r=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if typ.is_dir() &&
           entry.file_name()[..].to_str().map(|n| n.starts_with(".tmp"))
                                         .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }

    return Ok(());
}

fn clean_old(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let useful: HashSet<String> = try_msg!(
        read_visible_entries(&wrapper.project_root.join(".vagga")),
            "Can't read vagga directory: {err}")
        .into_iter()
        .filter_map(|path| read_link(&path)
             .map_err(|e| warn!("Can't readlink {:?}: {}", path, e))
             .ok()
             .and_then(|f| {
                 // The container name is next to the last component
                 f.iter().rev().nth(1)
                 .and_then(|x| x.to_str()).map(ToString::to_string)
             }))
        .collect();
    debug!("Useful images {:?}", useful);

    let roots = base.join(".roots");
    for entry in try_msg!(read_dir(&roots),
                         "Can't read dir {dir:?}: {err}", dir=roots)
    {
        let entry = try_msg!(entry,
                             "Can't read dir {dir:?}: {err}", dir=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if !typ.is_dir() {
            try_msg!(remove_file(&entry.path()),
                "Can't remove file {p:?}: {err}", p=entry.path());
        } else if !typ.is_dir() || entry.file_name()[..].to_str()
            .map(|n| !useful.contains(&n.to_string()))
            .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }

    return Ok(());
}

fn clean_transient(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let procfs = Path::new("/proc");
    for entry in try_msg!(read_dir(&base.join(".transient")),
                        "Can't read .vagga/.transient dir: {err}")
    {
        let entry = try_msg!(entry, "Error reading .vagga/transient: {err}");
        if let Some(fname) = entry.file_name()[..].to_str() {
            if let Some(idx) = fname.find('.') {
                if u32::from_str(&fname[idx+1..]).is_ok() &&
                    procfs.join(&fname[idx+1..]).exists()
                {
                    continue;
                }
            }
        }
        try!(clean_dir_wrapper(&entry.path(), dry_run));
    }

    return Ok(());
}

fn clean_unused(wrapper: &Wrapper, global: bool, duration: Option<Duration>,
    dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }

    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    let mut useful: HashSet<String> = HashSet::new();
    if let Some(duration) = duration {
        let unixtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let cut_off = (unixtime - duration).as_secs() as i64;
        ScanDir::dirs().skip_hidden(false).read("/vagga/base/.roots", |iter| {
            for (entry, name) in iter {
                let luse_path = entry.path().join("last_use");
                match metadata(&luse_path) {
                    Ok(ref meta) if meta.mtime() > cut_off => {
                        useful.insert(name);
                    }
                    Ok(_) => {}
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
                    Err(e) => {
                        error!("Error trying to stat {:?}: {}", luse_path, e);
                    }
                }
            }
        }).map_err(|e| {
            error!("Error reading `.vagga/.roots`: {}", e);
        }).ok();
    } else {
        for (name, _) in &wrapper.config.containers {
            if let Some(version) = try!(get_version_hash(name, wrapper)) {
                useful.insert(format!("{}.{}", name, &version[..8]));
            }
        }
    }
    debug!("Useful images {:?}", useful);

    let roots = Path::new("/vagga/base/.roots");
    for entry in try_msg!(read_dir(&roots),
                         "Can't read dir {dir:?}: {err}", dir=roots)
    {
        let entry = try_msg!(entry,
                             "Can't read dir {dir:?}: {err}", dir=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if !typ.is_dir() {
            try_msg!(remove_file(&entry.path()),
                "Can't remove file {p:?}: {err}", p=entry.path());
        } else if !typ.is_dir() || entry.file_name()[..].to_str()
            .map(|n| !useful.contains(&n.to_string()))
            .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }

    return Ok(());
}
<commit_msg>Check locks when doing cleaning<commit_after>use std::collections::HashSet;
use std::fs::{read_dir, read_link, remove_file, metadata};
use std::io::{self, stdout, stderr};
use std::ffi::OsStr;
use std::path::Path;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use std::os::unix::fs::MetadataExt;

use argparse::{ArgumentParser, PushConst, StoreTrue, StoreOption};
use scan_dir::ScanDir;
use humantime;

use super::setup;
use super::Wrapper;
use container::util::clean_dir;
use file_util::{read_visible_entries, Lock};
use wrapper::build::get_version_hash;

#[derive(Clone, Copy)]
enum Action {
    Temporary,
    Old,
    Unused,
    Everything,
    Transient,
}

pub fn clean_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut global = false;
    let mut dry_run = false;
    let mut actions = vec!();
    let mut duration = None::<humantime::Duration>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Performs various cleanup tasks
            ");
        ap.refer(&mut actions)
          .add_option(&["--tmp", "--tmp-folders"],
                PushConst(Action::Temporary),
                "Clean temporary containers (failed builds)")
          .add_option(&["--old", "--old-containers"], PushConst(Action::Old), "
                Clean old versions of containers (those which doesn't have a \
                symlink in .vagga)")
          .add_option(&["--unused"], PushConst(Action::Unused), "
                Clean unused containers, or versions thereof. (This is not \
                `--old` for historical reasons, we will probably merge the \
                commands later on)")
          .add_option(&["--transient"], PushConst(Action::Transient),
                "Clean unneeded transient folders (left from containers with
                 `write-mode` set to transient-something). The pid of process
                 is checked for liveness first.")
          .add_option(&["--everything"], PushConst(Action::Everything),
                "Clean whole `.vagga` folder. Useful when deleting a project.
                 With ``--global`` cleans whole storage-dir and cache-dir")
          .required();
        ap.refer(&mut global)
          .add_option(&["--global"], StoreTrue,
                "Apply cleanup command to all containers. Works only \
                if `storage-dir` is configured in settings");
        ap.refer(&mut dry_run)
          .add_option(&["-n", "--dry-run"], StoreTrue,
                "Dry run. Don't delete everything, just print");
        ap.refer(&mut duration)
          .add_option(&["--at-least"], StoreOption, "
            Only in combination with `--unused`. Treat as unused \
            containers that are unused for specified time, rather than \
            the ones not used by current version of config");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(1);
            }
        }
    }
    if global && wrapper.ext_settings.storage_dir.is_none() {
        error!("The --global flag is only meaningful if you configure \
            storage-dir in settings");
        return Ok(2);
    }
    let duration = duration.map(|x| x.into());
    for action in actions.iter() {
        let res = match *action {
            Action::Temporary => clean_temporary(wrapper, global, dry_run),
            Action::Old => clean_old(wrapper, global, dry_run),
            Action::Unused => {
                clean_unused(wrapper, global, duration, dry_run)
            }
            Action::Transient => clean_transient(wrapper, global, dry_run),
            _ => unimplemented!(),
        };
        match res {
            Ok(()) => {}
            Err(err) => {
                error!("Error cleaning up: {}", err);
                return Ok(3);
            }
        }
    }
    return Ok(0);
}

fn clean_dir_wrapper(path: &Path, dry_run: bool) -> Result<(), String> {
    // TODO(tailhook) chroot to dir for removing
    if dry_run {
        println!("Would remove {:?}", path);
    } else {
        let mut n = path.to_path_buf().into_os_string();
        n.push(".lock");
        let lock_name = Path::new(&n);
        let lock_guard = if lock_name.exists() {
            match Lock::exclusive(&lock_name) {
                Ok(x) => Some(x),
                Err(e) => {
                    error!("Failed to lock {:?}: {}, skipping", lock_name, e);
                    return Ok(());
                }
            }
        } else {
            None
        };
        debug!("Removing {:?}", path);
        try!(clean_dir(path, true));
        if let Some(_lock) = lock_guard {
            try!(remove_file(lock_name)
                .map_err(|e| format!("Error removing lock file {:?}: {}",
                    lock_name, e)));
        }
    }
    Ok(())
}

fn clean_temporary(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let roots = base.join(".roots");
    for entry in try_msg!(read_dir(&roots),
        "Can't read dir {r:?}: {err}", r=roots)
    {
        let entry = try_msg!(entry, "Can't read dir {r:?}: {err}", r=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if typ.is_dir() &&
           entry.file_name()[..].to_str().map(|n| n.starts_with(".tmp"))
                                         .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }

    return Ok(());
}

fn clean_old(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let useful: HashSet<String> = try_msg!(
        read_visible_entries(&wrapper.project_root.join(".vagga")),
            "Can't read vagga directory: {err}")
        .into_iter()
        .filter_map(|path| read_link(&path)
             .map_err(|e| warn!("Can't readlink {:?}: {}", path, e))
             .ok()
             .and_then(|f| {
                 // The container name is next to the last component
                 f.iter().rev().nth(1)
                 .and_then(|x| x.to_str()).map(ToString::to_string)
             }))
        .collect();

    info!("Useful images {:?}", useful);
    try!(clean_dirs_except(&base.join(".roots"), &useful, dry_run));

    return Ok(());
}

fn clean_transient(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let procfs = Path::new("/proc");
    for entry in try_msg!(read_dir(&base.join(".transient")),
                        "Can't read .vagga/.transient dir: {err}")
    {
        let entry = try_msg!(entry, "Error reading .vagga/transient: {err}");
        if let Some(fname) = entry.file_name()[..].to_str() {
            if let Some(idx) = fname.find('.') {
                if u32::from_str(&fname[idx+1..]).is_ok() &&
                    procfs.join(&fname[idx+1..]).exists()
                {
                    continue;
                }
            }
        }
        try!(clean_dir_wrapper(&entry.path(), dry_run));
    }

    return Ok(());
}

fn clean_dirs_except<P: AsRef<Path>>(roots: P, useful: &HashSet<String>,
    dry_run: bool)
    -> Result<(), String>
{
    let roots = roots.as_ref();
    for entry in try_msg!(read_dir(&roots),
                         "Can't read dir {dir:?}: {err}", dir=roots)
    {
        let entry = try_msg!(entry,
                             "Can't read dir {dir:?}: {err}", dir=roots);
        let path = entry.path();
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=path);
        if !typ.is_dir() {
            if path.extension() == Some(OsStr::new("lock")) &&
               path.with_extension("").is_dir()
            {
                debug!("Skipping lock file {:?}", path);
            } else {
                try_msg!(remove_file(&path),
                    "Can't remove file {p:?}: {err}", p=path);
            }
        } else if !typ.is_dir() || entry.file_name()[..].to_str()
            .map(|n| !useful.contains(&n.to_string()))
            .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }
    Ok(())
}

fn clean_unused(wrapper: &Wrapper, global: bool, duration: Option<Duration>,
    dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }

    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    let mut useful: HashSet<String> = HashSet::new();
    if let Some(duration) = duration {
        let unixtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let cut_off = (unixtime - duration).as_secs() as i64;
        ScanDir::dirs().skip_hidden(false).read("/vagga/base/.roots", |iter| {
            for (entry, name) in iter {
                let luse_path = entry.path().join("last_use");
                match metadata(&luse_path) {
                    Ok(ref meta) if meta.mtime() > cut_off => {
                        useful.insert(name);
                    }
                    Ok(_) => {}
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
                    Err(e) => {
                        error!("Error trying to stat {:?}: {}", luse_path, e);
                    }
                }
            }
        }).map_err(|e| {
            error!("Error reading `.vagga/.roots`: {}", e);
        }).ok();
    } else {
        for (name, _) in &wrapper.config.containers {
            if let Some(version) = try!(get_version_hash(name, wrapper)) {
                useful.insert(format!("{}.{}", name, &version[..8]));
            }
        }
    }
    info!("Useful images {:?}", useful);
    try!(clean_dirs_except("/vagga/base/.roots", &useful, dry_run));

    return Ok(());
}
<|endoftext|>


<commit_before>//! # Low-level access to the End Of Central Directory records of a ZIP archive

use std::iter::Iterator;

use super::super::api::Reader;
use super::super::utils::{Slice, skip};

use super::raw::EndOfCentralDirectoryReader;

/// An iterator over all potential End of Central Directory records within the
/// slice, iterating *backward* (from the end of the slice).
///
/// This is an iterator for two reasons:
///
/// - a correct archive may embed multiple "fake" such records within the
///   the comment field of the real record.
///
/// - a user may be interested in browsing all records in an archive, to see
///   its history.
///
/// To quickly get to the most likely End of Central Directory record, use the
/// `locate_end_of_central_directory` function below.
///
/// Note: this iterator does not attempt to validate the potential records in
/// any way.
#[derive(Debug)]
pub struct EndOfCentralDirectoryIterator<'a> {
    data: Slice<'a>,
    rpos: usize,
}

impl<'a> EndOfCentralDirectoryIterator<'a> {
    /// Returns an instance of EndOfCentralDirectoryIterator to search for End of Central
    /// Directory records within the slice.
    ///
    /// The most up-to-date record is in the last
    /// `EndOfCentralDirectoryReader::max_size()` bytes of the archive.
    pub fn new(slice: &'a [u8]) -> EndOfCentralDirectoryIterator<'a> {
        EndOfCentralDirectoryIterator {
            data: Slice::new(slice),
            rpos: Self::min_size().wrapping_sub(1)
        }
    }

    fn min_size() -> usize { EndOfCentralDirectoryReader::min_size() }
}

impl<'a> Iterator for EndOfCentralDirectoryIterator<'a> {
    type Item = EndOfCentralDirectoryReader<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        while self.rpos < self.data.len() {
            self.rpos = self.rpos.wrapping_add(1);

            let pos = self.data.len().wrapping_sub(self.rpos);

            if let Some(s) = self.data.slice(pos..pos.wrapping_add(4)) {
                if s.raw() == b"PK\x05\x06" {
                    return EndOfCentralDirectoryReader::new(
                        skip(self.data.raw(), pos)
                    );
                }
            }
        }

        None
    }
}

/// Returns the most likely End of Central Directory record in the archive.
///
/// The heuristic used is:
///
/// - to look, backward, in the last `EndOfCentralDirectoryReader::max_size()`
///   bytes of the archive.
///
/// - to pick the first record whose comment length field concords with the size
///   of the archive.
///
pub fn locate_end_of_central_directory<'a, R: ?Sized>(reader: &'a R)
    -> Option<EndOfCentralDirectoryReader<'a>>
    where R: Reader + 'a
{
    type EocdReader<'a> = EndOfCentralDirectoryReader<'a>;

    fn matches<'a>(eocd: &EocdReader<'a>, slice: &'a [u8]) -> bool {
        let comment_size = eocd.comment_size() as usize;

        let eocd_end = (eocd.raw().as_ptr() as usize)
            .wrapping_add(EocdReader::min_size())
            .wrapping_add(comment_size);

        let slice_end = (slice.as_ptr() as usize).wrapping_add(slice.len());

        eocd_end == slice_end
    }

    let slice = {
        use std::cmp::min;

        let end = reader.size();
        let start = end - min(EocdReader::max_size(), end);

        reader.get(start..end)
    };

    EndOfCentralDirectoryIterator::new(slice)
        .filter(|eocd| matches(eocd, slice))
        .next()
}

#[cfg(test)]
mod tests {
    use super::{EndOfCentralDirectoryIterator, locate_end_of_central_directory};
    use super::super::raw::EndOfCentralDirectoryReader as EocdReader;
    use super::super::super::utils::position;

    #[test]
    fn iter_eocd_none_on_too_small_slice() {
        let v = vec![0; EocdReader::min_size() - 1];
        for length in 0..v.len() {
            let mut it = EndOfCentralDirectoryIterator::new(&v[..length]);

            assert_eq!(it.next(), None);
        }
    }

    #[test]
    fn iter_ecod_none_on_maximum_size_zeroed_slice() {
        let v = vec![0; EocdReader::max_size()];
        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        assert_eq!(it.next(), None);
    }

    #[test]
    fn iter_eocd_once_on_minimum_size_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size()];
            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        if let Some(eocd) = it.next() {
            assert_eq!(position(eocd.raw(), &v[..]), Some(0));
        } else {
            assert!(false);
        }

        assert_eq!(it.next(), None);
    }

    #[test]
    fn iter_eocd_all_on_maximum_size_slice() {
        let max_pos = (EocdReader::max_size() - EocdReader::min_size()) / 4 * 4;

        let v = {
            let mut v = vec![0; EocdReader::max_size()];
            for i in 0..(max_pos / 4 + 1) {
                let base_index = i * 4;
                v[base_index + 3] = 0x06;
                v[base_index + 2] = 0x05;
                v[base_index + 1] = 0x4b;
                v[base_index + 0] = 0x50;
            }
            v
        };

        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        let mut pos = max_pos + 4;
        let mut total = 0;

        while let Some(eocd) = it.next() {
            assert_eq!(eocd.signature(), EocdReader::expected_signature());
            assert_eq!(position(eocd.raw(), &v[..]), Some(pos - 4));
            pos -= 4;
            total += 1;
        }

        assert_eq!(total, max_pos / 4 + 1);
    }

    #[test]
    fn locate_eocd_none_on_too_small_slice() {
        let v = vec![0; EocdReader::min_size() - 1];
        for length in 0..v.len() {
            assert_eq!(locate_end_of_central_directory(&v[..length]), None);
        }
    }

    #[test]
    fn locate_eocd_none_on_maximum_size_zeroed_slice() {
        let v = vec![0; EocdReader::max_size()];
        assert_eq!(locate_end_of_central_directory(&v[..]), None);
    }

    #[test]
    fn locate_eocd_on_minimum_size_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size()];
            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(0));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn locate_eocd_latest_on_maximum_size_full_slice() {
        let max_pos = (EocdReader::max_size() - EocdReader::min_size()) / 4 * 4;

        let v = {
            let mut v = vec![0; EocdReader::max_size()];
            for i in 0..(max_pos / 4 + 1) {
                let base_index = i * 4 + 3;
                v[base_index + 3] = 0x06;
                v[base_index + 2] = 0x05;
                v[base_index + 1] = 0x4b;
                v[base_index + 0] = 0x50;
            }
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(65535));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn locate_eocd_latest_valid_on_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size() + 8];
            v[28] = 0x01;   // EOCD [8..30), wrong comment field size
            v[24] = 0x04;   // EOCD [4..30), right comment field size   <=
            v[20] = 0x08;   // EOCD [0..30), right comment field size

            v[11] = 0x06;
            v[10] = 0x05;
            v[9] = 0x4b;
            v[8] = 0x50;

            v[7] = 0x06;
            v[6] = 0x05;
            v[5] = 0x4b;
            v[4] = 0x50;

            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(4));
        } else {
            assert!(false);
        }
    }
}
<commit_msg>Wrap comment at 80 characters mark<commit_after>//! # Low-level access to the End Of Central Directory records of a ZIP archive

use std::iter::Iterator;

use super::super::api::Reader;
use super::super::utils::{Slice, skip};

use super::raw::EndOfCentralDirectoryReader;

/// An iterator over all potential End of Central Directory records within the
/// slice, iterating *backward* (from the end of the slice).
///
/// This is an iterator for two reasons:
///
/// - a correct archive may embed multiple "fake" such records within the
///   the comment field of the real record.
///
/// - a user may be interested in browsing all records in an archive, to see
///   its history.
///
/// To quickly get to the most likely End of Central Directory record, use the
/// `locate_end_of_central_directory` function below.
///
/// Note: this iterator does not attempt to validate the potential records in
/// any way.
#[derive(Debug)]
pub struct EndOfCentralDirectoryIterator<'a> {
    data: Slice<'a>,
    rpos: usize,
}

impl<'a> EndOfCentralDirectoryIterator<'a> {
    /// Returns an instance of EndOfCentralDirectoryIterator to search for End
    /// of Central Directory records within the slice.
    ///
    /// The most up-to-date record is in the last
    /// `EndOfCentralDirectoryReader::max_size()` bytes of the archive.
    pub fn new(slice: &'a [u8]) -> EndOfCentralDirectoryIterator<'a> {
        EndOfCentralDirectoryIterator {
            data: Slice::new(slice),
            rpos: Self::min_size().wrapping_sub(1)
        }
    }

    fn min_size() -> usize { EndOfCentralDirectoryReader::min_size() }
}

impl<'a> Iterator for EndOfCentralDirectoryIterator<'a> {
    type Item = EndOfCentralDirectoryReader<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        while self.rpos < self.data.len() {
            self.rpos = self.rpos.wrapping_add(1);

            let pos = self.data.len().wrapping_sub(self.rpos);

            if let Some(s) = self.data.slice(pos..pos.wrapping_add(4)) {
                if s.raw() == b"PK\x05\x06" {
                    return EndOfCentralDirectoryReader::new(
                        skip(self.data.raw(), pos)
                    );
                }
            }
        }

        None
    }
}

/// Returns the most likely End of Central Directory record in the archive.
///
/// The heuristic used is:
///
/// - to look, backward, in the last `EndOfCentralDirectoryReader::max_size()`
///   bytes of the archive.
///
/// - to pick the first record whose comment length field concords with the size
///   of the archive.
///
pub fn locate_end_of_central_directory<'a, R: ?Sized>(reader: &'a R)
    -> Option<EndOfCentralDirectoryReader<'a>>
    where R: Reader + 'a
{
    type EocdReader<'a> = EndOfCentralDirectoryReader<'a>;

    fn matches<'a>(eocd: &EocdReader<'a>, slice: &'a [u8]) -> bool {
        let comment_size = eocd.comment_size() as usize;

        let eocd_end = (eocd.raw().as_ptr() as usize)
            .wrapping_add(EocdReader::min_size())
            .wrapping_add(comment_size);

        let slice_end = (slice.as_ptr() as usize).wrapping_add(slice.len());

        eocd_end == slice_end
    }

    let slice = {
        use std::cmp::min;

        let end = reader.size();
        let start = end - min(EocdReader::max_size(), end);

        reader.get(start..end)
    };

    EndOfCentralDirectoryIterator::new(slice)
        .filter(|eocd| matches(eocd, slice))
        .next()
}

#[cfg(test)]
mod tests {
    use super::{EndOfCentralDirectoryIterator, locate_end_of_central_directory};
    use super::super::raw::EndOfCentralDirectoryReader as EocdReader;
    use super::super::super::utils::position;

    #[test]
    fn iter_eocd_none_on_too_small_slice() {
        let v = vec![0; EocdReader::min_size() - 1];
        for length in 0..v.len() {
            let mut it = EndOfCentralDirectoryIterator::new(&v[..length]);

            assert_eq!(it.next(), None);
        }
    }

    #[test]
    fn iter_ecod_none_on_maximum_size_zeroed_slice() {
        let v = vec![0; EocdReader::max_size()];
        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        assert_eq!(it.next(), None);
    }

    #[test]
    fn iter_eocd_once_on_minimum_size_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size()];
            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        if let Some(eocd) = it.next() {
            assert_eq!(position(eocd.raw(), &v[..]), Some(0));
        } else {
            assert!(false);
        }

        assert_eq!(it.next(), None);
    }

    #[test]
    fn iter_eocd_all_on_maximum_size_slice() {
        let max_pos = (EocdReader::max_size() - EocdReader::min_size()) / 4 * 4;

        let v = {
            let mut v = vec![0; EocdReader::max_size()];
            for i in 0..(max_pos / 4 + 1) {
                let base_index = i * 4;
                v[base_index + 3] = 0x06;
                v[base_index + 2] = 0x05;
                v[base_index + 1] = 0x4b;
                v[base_index + 0] = 0x50;
            }
            v
        };

        let mut it = EndOfCentralDirectoryIterator::new(&v[..]);

        let mut pos = max_pos + 4;
        let mut total = 0;

        while let Some(eocd) = it.next() {
            assert_eq!(eocd.signature(), EocdReader::expected_signature());
            assert_eq!(position(eocd.raw(), &v[..]), Some(pos - 4));
            pos -= 4;
            total += 1;
        }

        assert_eq!(total, max_pos / 4 + 1);
    }

    #[test]
    fn locate_eocd_none_on_too_small_slice() {
        let v = vec![0; EocdReader::min_size() - 1];
        for length in 0..v.len() {
            assert_eq!(locate_end_of_central_directory(&v[..length]), None);
        }
    }

    #[test]
    fn locate_eocd_none_on_maximum_size_zeroed_slice() {
        let v = vec![0; EocdReader::max_size()];
        assert_eq!(locate_end_of_central_directory(&v[..]), None);
    }

    #[test]
    fn locate_eocd_on_minimum_size_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size()];
            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(0));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn locate_eocd_latest_on_maximum_size_full_slice() {
        let max_pos = (EocdReader::max_size() - EocdReader::min_size()) / 4 * 4;

        let v = {
            let mut v = vec![0; EocdReader::max_size()];
            for i in 0..(max_pos / 4 + 1) {
                let base_index = i * 4 + 3;
                v[base_index + 3] = 0x06;
                v[base_index + 2] = 0x05;
                v[base_index + 1] = 0x4b;
                v[base_index + 0] = 0x50;
            }
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(65535));
        } else {
            assert!(false);
        }
    }

    #[test]
    fn locate_eocd_latest_valid_on_slice() {
        let v = {
            let mut v = vec![0; EocdReader::min_size() + 8];
            v[28] = 0x01;   // EOCD [8..30), wrong comment field size
            v[24] = 0x04;   // EOCD [4..30), right comment field size   <=
            v[20] = 0x08;   // EOCD [0..30), right comment field size

            v[11] = 0x06;
            v[10] = 0x05;
            v[9] = 0x4b;
            v[8] = 0x50;

            v[7] = 0x06;
            v[6] = 0x05;
            v[5] = 0x4b;
            v[4] = 0x50;

            v[3] = 0x06;
            v[2] = 0x05;
            v[1] = 0x4b;
            v[0] = 0x50;
            v
        };

        if let Some(eocd) = locate_end_of_central_directory(&v[..]) {
            assert_eq!(position(eocd.raw(), &v[..]), Some(4));
        } else {
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix http example<commit_after><|endoftext|>


<commit_before>// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function so that it compiles and
// passes the tests... so many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Scroll down for hints :)

use std::error;
use std::fmt;
use std::io;

// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
    let mut line = String::new();
    b.read_line(&mut line);
    let num: i64 = line.trim().parse();
    let answer = PositiveNonzeroInteger::new(num);
    answer
}

// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<error::Error>> {
    let mut b = io::BufReader::new(s.as_bytes());
    read_and_validate(&mut b)
}

#[test]
fn test_success() {
    let x = test_with_str("42\n");
    assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}

#[test]
fn test_not_num() {
    let x = test_with_str("eleven billion\n");
    assert!(x.is_err());
}

#[test]
fn test_non_positive() {
    let x = test_with_str("-40\n");
    assert!(x.is_err());
}

#[test]
fn test_ioerror() {
    struct Broken;
    impl io::Read for Broken {
        fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
            Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
        }
    }
    let mut b = io::BufReader::new(Broken);
    assert!(read_and_validate(&mut b).is_err());
    assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        if value == 0 {
            Err(CreationError::Zero)
        } else if value < 0 {
            Err(CreationError::Negative)
        } else {
            Ok(PositiveNonzeroInteger(value as u64))
        }
    }
}

#[test]
fn test_positive_nonzero_integer_creation() {
    assert!(PositiveNonzeroInteger::new(10).is_ok());
    assert_eq!(
        Err(CreationError::Negative),
        PositiveNonzeroInteger::new(-10)
    );
    assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str((self as &error::Error).description())
    }
}

impl error::Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::Negative => "Negative",
            CreationError::Zero => "Zero",
        }
    }
}

// First hint: To figure out what type should go where the ??? is, take a look
// at the test helper function `test_with_str`, since it returns whatever
// `read_and_validate` returns and`test_with_str` has its signature fully
// specified.

// Next hint: There are three places in `read_and_validate` that we call a
// function that returns a `Result` (that is, the functions might fail).
// Apply the `?` operator on those calls so that we return immediately from
// `read_and_validate` if those function calls fail.

// Another hint: under the hood, the `?` operator calls `From::from`
// on the error value to convert it to a boxed trait object, a Box<error::Error>,
// which is polymorphic-- that means that lots of different kinds of errors
// can be returned from the same function because all errors act the same
// since they all implement the `error::Error` trait.
// Check out this section of the book:
// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator

// Another another hint: Note that because the `?` operator returns
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
// `read_and_validate` for *its* success case, we'll have to rewrap a value
// that we got from the return value of a `?`ed call in an `Ok`-- this will
// look like `Ok(something)`.

// Another another another hint: `Result`s must be "used", that is, you'll
// get a warning if you don't handle a `Result` that you get in your
// function. Read more about that in the `std::result` module docs:
// https://doc.rust-lang.org/std/result/#results-must-be-used
<commit_msg>Auto merge of #212 - sosnowski:fix/add-dyn-trait, r=komaeda<commit_after>// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function so that it compiles and
// passes the tests... so many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Scroll down for hints :)

use std::error;
use std::fmt;
use std::io;

// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut dyn io::BufRead) -> Result<PositiveNonzeroInteger, ???> {
    let mut line = String::new();
    b.read_line(&mut line);
    let num: i64 = line.trim().parse();
    let answer = PositiveNonzeroInteger::new(num);
    answer
}

// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<dyn error::Error>> {
    let mut b = io::BufReader::new(s.as_bytes());
    read_and_validate(&mut b)
}

#[test]
fn test_success() {
    let x = test_with_str("42\n");
    assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}

#[test]
fn test_not_num() {
    let x = test_with_str("eleven billion\n");
    assert!(x.is_err());
}

#[test]
fn test_non_positive() {
    let x = test_with_str("-40\n");
    assert!(x.is_err());
}

#[test]
fn test_ioerror() {
    struct Broken;
    impl io::Read for Broken {
        fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
            Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
        }
    }
    let mut b = io::BufReader::new(Broken);
    assert!(read_and_validate(&mut b).is_err());
    assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        if value == 0 {
            Err(CreationError::Zero)
        } else if value < 0 {
            Err(CreationError::Negative)
        } else {
            Ok(PositiveNonzeroInteger(value as u64))
        }
    }
}

#[test]
fn test_positive_nonzero_integer_creation() {
    assert!(PositiveNonzeroInteger::new(10).is_ok());
    assert_eq!(
        Err(CreationError::Negative),
        PositiveNonzeroInteger::new(-10)
    );
    assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str((self as &dyn error::Error).description())
    }
}

impl error::Error for CreationError {
    fn description(&self) -> &str {
        match *self {
            CreationError::Negative => "Negative",
            CreationError::Zero => "Zero",
        }
    }
}

// First hint: To figure out what type should go where the ??? is, take a look
// at the test helper function `test_with_str`, since it returns whatever
// `read_and_validate` returns and`test_with_str` has its signature fully
// specified.

// Next hint: There are three places in `read_and_validate` that we call a
// function that returns a `Result` (that is, the functions might fail).
// Apply the `?` operator on those calls so that we return immediately from
// `read_and_validate` if those function calls fail.

// Another hint: under the hood, the `?` operator calls `From::from`
// on the error value to convert it to a boxed trait object, a Box<dyn error::Error>,
// which is polymorphic-- that means that lots of different kinds of errors
// can be returned from the same function because all errors act the same
// since they all implement the `error::Error` trait.
// Check out this section of the book:
// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator

// Another another hint: Note that because the `?` operator returns
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
// `read_and_validate` for *its* success case, we'll have to rewrap a value
// that we got from the return value of a `?`ed call in an `Ok`-- this will
// look like `Ok(something)`.

// Another another another hint: `Result`s must be "used", that is, you'll
// get a warning if you don't handle a `Result` that you get in your
// function. Read more about that in the `std::result` module docs:
// https://doc.rust-lang.org/std/result/#results-must-be-used
<|endoftext|>


<commit_before>use std::str::Chars;
use std::iter::Peekable;
use std::iter::Enumerate;

use ::syntax::lexer::{Token, Lexer, Span};
use ::syntax::next_is;

pub struct PythonSyntax;

impl Lexer for PythonSyntax {
    fn handle_char(&self, ch: char, mut iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize, idx: usize) -> Option<Span> {
        match ch {
            '#' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("#");
                while let Some(&(e, c)) = iter.peek() {
                    if c == '\n' { break }
                    end = e;
                    s.push(iter.next().unwrap().1)
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::SingleLineComment(s),
                });
            }

            '@' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("@");
                while let Some(&(e, c)) = iter.peek() {
                    if c == '\n' { break }
                    end = e;
                    s.push(iter.next().unwrap().1)
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::Attribute(s),
                });
            }

            '"' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("\"");
                while let Some(&(e, c)) = iter.peek() {
                    end = e;
                    s.push(iter.next().unwrap().1);
                    if c == '"' {
                        break;
                    }
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::String(s),
                });
            }

            '\'' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("'");
                while let Some(&(e, c)) = iter.peek() {
                    end = e;
                    s.push(iter.next().unwrap().1);
                    if c == '\'' {
                        break;
                    }
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::String(s),
                });
            }

            _ => None,
        }

    }

    fn handle_ident(&self, ch: char, iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize) -> Span {
        let mut ident = String::new();
        ident.push(ch);
        let start = iter.peek().unwrap().0 - 1;

        while self.is_ident(iter.peek()) {
            ident.push(iter.next().unwrap().1)
        }

        Span {
            y_pos: y_pos,
            start: start,
            end: start + ident.len() - 1,
            token: Token::Ident(ident),
        }
    }
}

<commit_msg>Refactor python string matching<commit_after>use std::str::Chars;
use std::iter::Peekable;
use std::iter::Enumerate;

use ::syntax::lexer::{Token, Lexer, Span};
use ::syntax::next_is;

pub struct PythonSyntax;

impl Lexer for PythonSyntax {
    fn handle_char(&self, ch: char, mut iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize, idx: usize) -> Option<Span> {
        match ch {
            '#' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("#");
                while let Some(&(e, c)) = iter.peek() {
                    if c == '\n' { break }
                    end = e;
                    s.push(iter.next().unwrap().1)
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::SingleLineComment(s),
                });
            }

            '@' => {
                let st = idx;
                let mut end = idx;
                let mut s = String::from("@");
                while let Some(&(e, c)) = iter.peek() {
                    if c == '\n' { break }
                    end = e;
                    s.push(iter.next().unwrap().1)
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::Attribute(s),
                });
            }

            ch if ch == '"' || ch == '\'' => {
                let st = idx;
                let mut end = idx;
                let mut s= format!("{}", ch);
                while let Some(&(e, c)) = iter.peek() {
                    end = e;
                    s.push(iter.next().unwrap().1);
                    if c == ch {
                        break;
                    }
                }
                return Some(Span {
                    y_pos: y_pos,
                    start: st,
                    end: end,
                    token: Token::String(s),
                });
            }

            _ => None,
        }

    }

    fn handle_ident(&self, ch: char, iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize) -> Span {
        let mut ident = String::new();
        ident.push(ch);
        let start = iter.peek().unwrap().0 - 1;

        while self.is_ident(iter.peek()) {
            ident.push(iter.next().unwrap().1)
        }

        Span {
            y_pos: y_pos,
            start: start,
            end: start + ident.len() - 1,
            token: Token::Ident(ident),
        }
    }
}

<|endoftext|>