<commit_before>use cgmath;
use glium;
use glutin;
use std;
use rand;
use time;

use scene;

fn cl_float3(x: f32, y: f32, z: f32) -> scene::cl_float3 {
  scene::cl_float3::new([x, y, z])
}

fn solid_color(r: f32, g: f32, b: f32) -> scene::texture::T {
  scene::texture::SolidColor(cl_float3(r, g, b)).to_texture()
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RGB {
  pub r: f32,
  pub g: f32,
  pub b: f32,
}

unsafe impl Send for RGB {}

unsafe impl glium::texture::PixelValue for RGB {
  fn get_format() -> glium::texture::ClientFormat {
    glium::texture::ClientFormat::F32F32F32
  }
}

pub const WINDOW_WIDTH: u32 = 800;
pub const WINDOW_HEIGHT: u32 = 600;

pub fn main() {
  use glium::DisplayBuild;

  let window =
    glutin::WindowBuilder::new()
    .with_dimensions(WINDOW_WIDTH, WINDOW_HEIGHT)
    .build_glium()
    .unwrap();

  // building the vertex buffer, which contains all the vertices that we will draw
  let vertex_buffer = {
    #[derive(Copy, Clone)]
    struct Vertex {
      position: [f32; 2],
      tex_coords: [f32; 2],
    }

    implement_vertex!(Vertex, position, tex_coords);

    glium::VertexBuffer::new(&window,
      &[
        Vertex { position: [-1.0, -1.0], tex_coords: [0.0, 0.0] },
        Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 1.0] },
        Vertex { position: [ 1.0,  1.0], tex_coords: [1.0, 1.0] },
        Vertex { position: [ 1.0, -1.0], tex_coords: [1.0, 0.0] }
      ]
    ).unwrap()
  };

  // building the index buffer
  let index_buffer =
    glium::IndexBuffer::new(
      &window,
      glium::index::PrimitiveType::TriangleStrip,
      &[1 as u16, 2, 0, 3],
    ).unwrap();

  // compiling shaders and linking them together
  let program = program!(&window,
    330 => {
      vertex: "
        #version 330

        uniform mat4 matrix;

        in vec2 position;
        in vec2 tex_coords;

        out vec2 v_tex_coords;

        void main() {
          gl_Position = matrix * vec4(position, 0.0, 1.0);
          v_tex_coords = tex_coords;
        }
      ",

      fragment: "
        #version 330
        uniform sampler2D tex;
        in vec2 v_tex_coords;
        out vec4 f_color;

        void main() {
          f_color = texture(tex, v_tex_coords);
        }
      "
    },
  ).unwrap();

  let mut scene =
    scene::T {
      objects       :
        vec!(
          // red ball
          scene::Object { center: cl_float3(-4.0,   -1.0,  -5.0), radius:   1.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 1.0 , texture: solid_color(1.0, 0.0, 0.0) },
          // blue ball
          scene::Object { center: cl_float3(-0.5,   -1.0,  -5.0), radius:   1.0, emittance:  0.0, reflectance: 0.1, transmittance: 0.9, diffuseness: 0.01, texture: solid_color(0.0, 0.6, 1.0) },
          // frosted glass ball
          scene::Object { center: cl_float3(-0.7,   -0.5,  -1.5), radius:   0.5, emittance:  0.0, reflectance: 0.1, transmittance: 0.8, diffuseness: 0.04, texture: solid_color(0.9, 0.9, 1.0) },
          // glass ball
          scene::Object { center: cl_float3( 0.2,   -0.5,  -1.0), radius:   0.5, emittance:  0.0, reflectance: 0.1, transmittance: 0.9, diffuseness: 0.0 , texture: solid_color(0.9, 0.9, 1.0) },
          // brass ball
          scene::Object { center: cl_float3( 3.0,    1.5, -10.0), radius:   4.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.1 , texture: solid_color(1.0, 0.4, 0.1) },
          // small mirror ball
          scene::Object { center: cl_float3( 3.0,   -1.0,  -3.5), radius:   1.0, emittance:  0.0, reflectance: 0.9, transmittance: 0.0, diffuseness: 0.0 , texture: solid_color(1.0, 1.0, 1.0) },
          // light
          scene::Object { center: cl_float3(-9.0,   10.0,   0.0), radius:   1.0, emittance:  1.0, reflectance: 0.0, transmittance: 1.0, diffuseness: 0.0 , texture: solid_color(0.9, 0.9, 1.0) },
          // walls
          scene::Object { center: cl_float3( 0.0,    0.0,   0.0), radius:  20.0, emittance:  0.2, reflectance: 0.0, transmittance: 0.0, diffuseness: 1.0 , texture: solid_color(1.0, 1.0, 1.0) },
          // floor
          scene::Object { center: cl_float3( 0.0, -102.0,   0.0), radius: 100.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.02, texture: scene::texture::Wood::to_texture() },
        ),
      fovy          : std::f32::consts::FRAC_PI_2,
      eye           : cgmath::Vector3::new(0.0, 0.0,  0.0),
      look          : cgmath::Vector3::new(0.0, 0.0, -1.0),
      up            : cgmath::Vector3::new(0.0, 1.0,  0.0),
    };

  let scale = 1 << 0;
  let w = scale * WINDOW_WIDTH;
  let h = scale * WINDOW_HEIGHT;

  let mut make_random_seed: rand::XorShiftRng =
    rand::SeedableRng::from_seed([0x12345678, 0x9abcdef0, 0x89765432, 0x12324121]);

  loop {
    let before = time::precise_time_ns();
    let rendered = scene.render(w, h, rand::Rng::next_u64(&mut make_random_seed));
    let after = time::precise_time_ns();
    println!("Render took {:?}ms", (after - before) as f32 / 1_000_000.0);

    let texture =
      glium::texture::Texture2d::with_format(
        &window,
        glium::texture::RawImage2d {
          data: std::borrow::Cow::Owned(rendered),
          width: w,
          height: h,
          format: glium::texture::ClientFormat::F32F32F32,
        },
        glium::texture::UncompressedFloatFormat::F32F32F32,
        glium::texture::MipmapsOption::AutoGeneratedMipmaps,
      )
      .unwrap();

    use glium::Surface;

    // building the uniforms
    let uniforms = uniform! {
      matrix: [
        [1.0, 0.0, 0.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 0.0, 0.0, 1.0f32]
      ],
      tex:
        glium::uniforms::Sampler::new(&texture)
        .magnify_filter(glium::uniforms::MagnifySamplerFilter::Linear)
        .minify_filter(glium::uniforms::MinifySamplerFilter::LinearMipmapLinear)
    };

    let mut target = window.draw();
    target.clear_color(0.0, 0.0, 0.0, 0.0);
    target.draw(&vertex_buffer, &index_buffer, &program, &uniforms, &Default::default()).unwrap();
    target.finish().unwrap();

    for event in window.poll_events() {
      match event {
        glutin::Event::Closed => return,
        glutin::Event::KeyboardInput(_, _, Some(key)) => {
          match key {
            glutin::VirtualKeyCode::W => scene.eye = scene.eye + scene.look,
            glutin::VirtualKeyCode::S => scene.eye = scene.eye - scene.look,
            _ => {},
          }
        },
        _ => {},
      }
    }
  }
}
<commit_msg>draw function<commit_after>use cgmath;
use glium;
use glutin;
use std;
use rand;
use time;

use scene;

fn cl_float3(x: f32, y: f32, z: f32) -> scene::cl_float3 {
  scene::cl_float3::new([x, y, z])
}

fn solid_color(r: f32, g: f32, b: f32) -> scene::texture::T {
  scene::texture::SolidColor(cl_float3(r, g, b)).to_texture()
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RGB {
  pub r: f32,
  pub g: f32,
  pub b: f32,
}

unsafe impl Send for RGB {}

unsafe impl glium::texture::PixelValue for RGB {
  fn get_format() -> glium::texture::ClientFormat {
    glium::texture::ClientFormat::F32F32F32
  }
}

pub const WINDOW_WIDTH: u32 = 800;
pub const WINDOW_HEIGHT: u32 = 600;

pub fn main() {
  use glium::DisplayBuild;

  let window =
    glutin::WindowBuilder::new()
    .with_dimensions(WINDOW_WIDTH, WINDOW_HEIGHT)
    .build_glium()
    .unwrap();

  let mut scene =
    scene::T {
      objects       :
        vec!(
          // red ball
          scene::Object { center: cl_float3(-4.0,   -1.0,  -5.0), radius:   1.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 1.0 , texture: solid_color(1.0, 0.0, 0.0) },
          // blue ball
          scene::Object { center: cl_float3(-0.5,   -1.0,  -5.0), radius:   1.0, emittance:  0.0, reflectance: 0.1, transmittance: 0.9, diffuseness: 0.01, texture: solid_color(0.0, 0.6, 1.0) },
          // frosted glass ball
          scene::Object { center: cl_float3(-0.7,   -0.5,  -1.5), radius:   0.5, emittance:  0.0, reflectance: 0.1, transmittance: 0.8, diffuseness: 0.04, texture: solid_color(0.9, 0.9, 1.0) },
          // glass ball
          scene::Object { center: cl_float3( 0.2,   -0.5,  -1.0), radius:   0.5, emittance:  0.0, reflectance: 0.1, transmittance: 0.9, diffuseness: 0.0 , texture: solid_color(0.9, 0.9, 1.0) },
          // brass ball
          scene::Object { center: cl_float3( 3.0,    1.5, -10.0), radius:   4.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.1 , texture: solid_color(1.0, 0.4, 0.1) },
          // small mirror ball
          scene::Object { center: cl_float3( 3.0,   -1.0,  -3.5), radius:   1.0, emittance:  0.0, reflectance: 0.9, transmittance: 0.0, diffuseness: 0.0 , texture: solid_color(1.0, 1.0, 1.0) },
          // light
          scene::Object { center: cl_float3(-9.0,   10.0,   0.0), radius:   1.0, emittance:  1.0, reflectance: 0.0, transmittance: 1.0, diffuseness: 0.0 , texture: solid_color(0.9, 0.9, 1.0) },
          // walls
          scene::Object { center: cl_float3( 0.0,    0.0,   0.0), radius:  20.0, emittance:  0.2, reflectance: 0.0, transmittance: 0.0, diffuseness: 1.0 , texture: solid_color(1.0, 1.0, 1.0) },
          // floor
          scene::Object { center: cl_float3( 0.0, -102.0,   0.0), radius: 100.0, emittance:  0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.02, texture: scene::texture::Wood::to_texture() },
        ),
      fovy          : std::f32::consts::FRAC_PI_2,
      eye           : cgmath::Vector3::new(0.0, 0.0,  0.0),
      look          : cgmath::Vector3::new(0.0, 0.0, -1.0),
      up            : cgmath::Vector3::new(0.0, 1.0,  0.0),
    };

  let scale = 1 << 0;
  let w = scale * WINDOW_WIDTH;
  let h = scale * WINDOW_HEIGHT;

  let mut make_random_seed: rand::XorShiftRng =
    rand::SeedableRng::from_seed([0x12345678, 0x9abcdef0, 0x89765432, 0x12324121]);

  loop {
    let before = time::precise_time_ns();
    let rendered = scene.render(w, h, rand::Rng::next_u64(&mut make_random_seed));
    let after = time::precise_time_ns();
    println!("Render took {:?}ms", (after - before) as f32 / 1_000_000.0);

    let texture =
      glium::texture::Texture2d::with_format(
        &window,
        glium::texture::RawImage2d {
          data: std::borrow::Cow::Owned(rendered),
          width: w,
          height: h,
          format: glium::texture::ClientFormat::F32F32F32,
        },
        glium::texture::UncompressedFloatFormat::F32F32F32,
        glium::texture::MipmapsOption::AutoGeneratedMipmaps,
      )
      .unwrap();

    draw_from_texture(&window, &texture);

    for event in window.poll_events() {
      match event {
        glutin::Event::Closed => return,
        glutin::Event::KeyboardInput(_, _, Some(key)) => {
          println!("hello world");
          match key {
            glutin::VirtualKeyCode::W => scene.eye = scene.eye + scene.look,
            glutin::VirtualKeyCode::S => scene.eye = scene.eye - scene.look,
            _ => {},
          }
        },
        _ => {},
      }
    }
  }
}

fn draw_from_texture(
  window: &glium::backend::glutin_backend::GlutinFacade,
  texture: &glium::texture::Texture2d,
) -> Result<(), ()>
{
  use glium::Surface;

  // building the vertex buffer, which contains all the vertices that we will draw
  let vertex_buffer = {
    #[derive(Copy, Clone)]
    struct Vertex {
      position: [f32; 2],
      tex_coords: [f32; 2],
    }

    implement_vertex!(Vertex, position, tex_coords);

    glium::VertexBuffer::new(
      window,
      &[
        Vertex { position: [-1.0, -1.0], tex_coords: [0.0, 0.0] },
        Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 1.0] },
        Vertex { position: [ 1.0,  1.0], tex_coords: [1.0, 1.0] },
        Vertex { position: [ 1.0, -1.0], tex_coords: [1.0, 0.0] }
      ]
    ).unwrap()
  };

  // building the index buffer
  let index_buffer =
    glium::IndexBuffer::new(
      window,
      glium::index::PrimitiveType::TriangleStrip,
      &[1 as u16, 2, 0, 3],
    ).unwrap();

  // compiling shaders and linking them together
  let program = program!(window,
    330 => {
      vertex: "
        #version 330

        uniform mat4 matrix;

        in vec2 position;
        in vec2 tex_coords;

        out vec2 v_tex_coords;

        void main() {
          gl_Position = matrix * vec4(position, 0.0, 1.0);
          v_tex_coords = tex_coords;
        }
      ",

      fragment: "
        #version 330
        uniform sampler2D tex;
        in vec2 v_tex_coords;
        out vec4 f_color;

        void main() {
          f_color = texture(tex, v_tex_coords);
        }
      "
    },
  ).unwrap();

  // building the uniforms
  let uniforms = uniform! {
    matrix: [
      [1.0, 0.0, 0.0, 0.0],
      [0.0, 1.0, 0.0, 0.0],
      [0.0, 0.0, 1.0, 0.0],
      [0.0, 0.0, 0.0, 1.0f32]
    ],
    tex:
      glium::uniforms::Sampler::new(texture)
      .magnify_filter(glium::uniforms::MagnifySamplerFilter::Linear)
      .minify_filter(glium::uniforms::MinifySamplerFilter::LinearMipmapLinear)
  };

  let mut target = window.draw();
  target.draw(
    &vertex_buffer,
    &index_buffer,
    &program,
    &uniforms,
    &Default::default(),
  ).unwrap();
  target.finish().unwrap();

  Ok(())
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::io::Write;

#[macro_use]
mod macros;

mod buffered_iterator;
mod ast;
mod scanning;
mod parsing;
mod running;

use ast::*;

fn main() {
	for arg in env::args() {
		println!("{}", arg);
	}

	loop {
		let mut line = String::new();

		print!("> ");
		io::stdout().flush().expect("Could not flush stdout");

		match io::stdin().read_line(&mut line) {
			Ok(_) => {
				match parsing::parse(&line) {
					Some(Ast::Command(Command::Exit)) => break,
					Some(Ast::Expression(expr)) => {
						match expr.run() {
							Ok(v) => println!("  {}", v),
							Err(msg) => println!("{}", msg),
						}
					},
					None => {}
				}
			}
			Err(msg) => println!("error: {}", msg),
		}
	} // loop
} // main
<commit_msg>Add support for passing equations via args<commit_after>use std::env;
use std::io;
use std::io::Write;

#[macro_use]
mod macros;

mod buffered_iterator;
mod ast;
mod scanning;
mod parsing;
mod running;

use ast::*;

fn parse_args() {
	for arg in env::args().skip(1) {
		match parsing::parse(&arg) {
			Some(Ast::Expression(expr)) => {
				match expr.run() {
					Ok(v) => println!("{}", v),
					Err(msg) => println!("{}", msg),
				}
			},
			_ => {}
		} // match
	} // for (arg)
} // parse_args

fn repl() {
	loop {
		let mut line = String::new();

		print!("> ");
		io::stdout().flush().expect("Could not flush stdout");

		match io::stdin().read_line(&mut line) {
			Ok(_) => {
				match parsing::parse(&line) {
					Some(Ast::Command(Command::Exit)) => break,
					Some(Ast::Expression(expr)) => {
						match expr.run() {
							Ok(v) => println!("  {}", v),
							Err(msg) => println!("{}", msg),
						}
					},
					None => {}
				}
			}
			Err(msg) => println!("error: {}", msg),
		}
	} // loop
} // repl

fn main() {
	if env::args().len() > 1 {
		return parse_args();
	}

	repl();
} // main
<|endoftext|>


<commit_before>use std::env;
extern crate i3ipc;
use i3ipc::I3Connection;
use i3ipc::reply::{Node, NodeType};

fn get_nodes<'a, F>(tree: &'a Node, condition: &F) -> Option<Vec<&'a Node>>
    where F: Fn(&Node) -> bool
{
    for node in &tree.nodes {
        if node.focused {
            let r = vec![node];
            return Some(r);
        }
        if let Some(mut n) = get_nodes(&node, condition) {
            n.push(node);
            return Some(n);
        }
    }
    return None;
}

fn get_current_tab<'a>(nodes: &[&'a Node]) -> &'a Node {
    let i = nodes.iter().position(|&n| {
        match n.nodetype { NodeType::Con => false, _ => true }
    }).unwrap();
    // i is Workspace
    // i - 1 is "Root container" for workspace tabs
    // i - 2 is current tab
    nodes[i - 2]
}

fn focus_child(c: &mut I3Connection) -> bool {
    let r = c.command("focus child").unwrap();
    for o in r.outcomes {
        if o.success == false {
            return false;
        }
    }
    return true;
}

fn superfocus(c: &mut I3Connection, direction: &str) {
    let tree = c.get_tree().unwrap();
    let nodes = get_nodes(
        &tree, &|n: &Node| { return n.focused; }
    ).expect("Can not find focused window. Maybe focused window is floating?");
    let current_tab = get_current_tab(&nodes);
    let focus_tab_msg = format!("[con_id=\"{}\"] focus", current_tab.id);
    c.command(&focus_tab_msg).unwrap();
    let focus_direction_msg = "focus ".to_string() + direction;
    c.command(&focus_direction_msg).unwrap();
    while focus_child(c) {}
}

fn main() {
    let direction = env::args().nth(1).expect("Missing direction argument");
    let mut connection = I3Connection::connect().unwrap();
    superfocus(&mut connection, &direction);
}
<commit_msg>Change initial focus only when needed<commit_after>use std::env;
extern crate i3ipc;
use i3ipc::I3Connection;
use i3ipc::reply::{Node, NodeType};

fn get_nodes<'a, F>(tree: &'a Node, condition: &F) -> Option<Vec<&'a Node>>
    where F: Fn(&Node) -> bool
{
    for node in &tree.nodes {
        if node.focused {
            let r = vec![node];
            return Some(r);
        }
        if let Some(mut n) = get_nodes(&node, condition) {
            n.push(node);
            return Some(n);
        }
    }
    return None;
}

fn get_current_tab<'a>(nodes: &[&'a Node]) -> &'a Node {
    let i = nodes.iter().position(|&n| {
        match n.nodetype { NodeType::Con => false, _ => true }
    }).unwrap();
    // i is Workspace
    // i - 1 is "Root container" for workspace tabs
    // i - 2 is current tab
    nodes[i - 2]
}

fn focus_child(c: &mut I3Connection) -> bool {
    let r = c.command("focus child").unwrap();
    for o in r.outcomes {
        if o.success == false {
            return false;
        }
    }
    return true;
}

fn superfocus(c: &mut I3Connection, direction: &str) {
    let tree = c.get_tree().unwrap();
    let nodes = get_nodes(
        &tree, &|n: &Node| { return n.focused; }
    ).expect("Can not find focused window. Maybe focused window is floating?");
    let current_tab = get_current_tab(&nodes);
    if current_tab.id != nodes[0].id {
        let focus_tab_msg = format!("[con_id=\"{}\"] focus", current_tab.id);
        c.command(&focus_tab_msg).unwrap();
    }
    let focus_direction_msg = "focus ".to_string() + direction;
    c.command(&focus_direction_msg).unwrap();
    while focus_child(c) {}
}

fn main() {
    let direction = env::args().nth(1).expect("Missing direction argument");
    let mut connection = I3Connection::connect().unwrap();
    superfocus(&mut connection, &direction);
}
<|endoftext|>


<commit_before>use diesel::result::Error;
use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;

pub fn get_notes(conn: &PgConnection) -> Result<Vec<Note>, Error> {
    use schema::notes::dsl::*;

    notes.load::<Note>(conn)
}

pub fn create_note(conn: &PgConnection, note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::insert(&note).into(notes::table).get_result(conn)
}

pub fn delete_note(conn: &PgConnection, id: i32) -> Result<usize, Error> {
    use schema::notes;

    diesel::delete(notes::table.find(id))
        .execute(conn)
}

pub fn update_note(conn: &PgConnection, id: i32, updated_note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::update(notes::table.find(id))
        .set(&updated_note)
        .get_result::<Note>(conn)
}
<commit_msg>Added a get_note function<commit_after>use diesel::result::Error;
use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;

pub fn get_note(conn: &PgConnection, id: i32) -> Result<Note, Error> {
    use schema::notes;

    notes::table.find(id).first::<Note>(conn)
}

pub fn get_notes(conn: &PgConnection) -> Result<Vec<Note>, Error> {
    use schema::notes;

    notes::table.load::<Note>(conn)
}

pub fn create_note(conn: &PgConnection, note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::insert(&note).into(notes::table).get_result(conn)
}

pub fn delete_note(conn: &PgConnection, id: i32) -> Result<usize, Error> {
    use schema::notes;

    diesel::delete(notes::table.find(id))
        .execute(conn)
}

pub fn update_note(conn: &PgConnection, id: i32, updated_note: NoteData) -> Result<Note, Error> {
    use schema::notes;

    diesel::update(notes::table.find(id))
        .set(&updated_note)
        .get_result::<Note>(conn)
}
<|endoftext|>


<commit_before>//! Plan in FFTW
//!
//! See also [Using Plans] in the original document
//! [Using Plans]: http://www.fftw.org/fftw3_doc/Using-Plans.html

use array::{alignment_of, Alignment};
use error::*;
use ffi::*;
use types::{c32, c64, Flag, Sign};

use std::marker::PhantomData;

pub type C2CPlan64 = Plan<c64, c64, Plan64>;
pub type C2CPlan32 = Plan<c32, c32, Plan32>;
pub type R2CPlan64 = Plan<f64, c64, Plan64>;
pub type R2CPlan32 = Plan<f32, c32, Plan32>;
pub type C2RPlan64 = Plan<c64, f64, Plan64>;
pub type C2RPlan32 = Plan<c32, f32, Plan32>;

/// Typed wrapper of `fftw_plan`
///
/// The plan in FFTW manages the contains all information necessary to compute the transform,
/// including the pointers to the input and output arrays.
/// However, this wrapper *does not modify* this pointer once after the plan is created
/// since it should be *unsafe* in terms of Rust.
/// Instead, this plan executes a transform for different arrays with [new-array execute functions]
/// with related associated functions, e.g. `C2CPlan::c2c`.
///
/// [new-array execute functions]: http://www.fftw.org/fftw3_doc/New_002darray-Execute-Functions.html
pub struct Plan<A, B, Plan: PlanSpec> {
    plan: Plan,
    input: (usize, Alignment),
    output: (usize, Alignment),
    phantom: PhantomData<(A, B)>,
}

impl<A, B, P: PlanSpec> Drop for Plan<A, B, P> {
    fn drop(&mut self) {
        self.plan.destroy();
    }
}

impl<A, B, P: PlanSpec> Plan<A, B, P> {
    fn check(&self, in_: &[A], out: &[B]) -> Result<()> {
        if self.input != slice_info(in_) {
            return Err(Error::InputArrayMismatch {
                expect: self.input,
                actual: slice_info(in_),
            });
        }
        if self.output != slice_info(out) {
            return Err(Error::OutputArrayMismatch {
                expect: self.output,
                actual: slice_info(out),
            });
        }
        Ok(())
    }
}

/// Trait for Plan makers
pub trait PlanSpec: Clone + Copy {
    fn validate(self) -> Result<Self>;
    fn destroy(self);
    fn print(self);
}

/// Marker for 64-bit floating point FFT
pub type Plan64 = fftw_plan;
/// Marker for 32-bit floating point FFT
pub type Plan32 = fftwf_plan;

/// Trait for the plan of Complex-to-Complex transformation
pub trait C2CPlan: Sized {
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-complex transform
    fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()>;
}

/// Trait for the plan of Real-to-Complex transformation
pub trait R2CPlan: Sized {
    type Real;
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Real],
        out: &mut [Self::Complex],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute real-to-complex transform
    fn r2c(&mut self, in_: &mut [Self::Real], out: &mut [Self::Complex]) -> Result<()>;
}

/// Trait for the plan of Complex-to-Real transformation
pub trait C2RPlan: Sized {
    type Real;
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Real],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-real transform
    fn c2r(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Real]) -> Result<()>;
}

macro_rules! impl_c2c {
    ($C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl C2CPlan for Plan<$C, $C, $Plan> {
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Complex],
                out: &mut [Self::Complex],
                sign: Sign,
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    sign as i32, flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()> {
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_c2c!

impl_c2c!(c64, Plan64; fftw_plan_dft, fftw_execute_dft);
impl_c2c!(c32, Plan32; fftwf_plan_dft, fftwf_execute_dft);

macro_rules! impl_r2c {
    ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl R2CPlan for Plan<$R, $C, $Plan> {
            type Real = $R;
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Real],
                out: &mut [Self::Complex],
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn r2c(&mut self, in_: &mut [Self::Real], out: &mut [Self::Complex]) -> Result<()> {
                self.check(in_, out)?;
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_r2c!

impl_r2c!(f64, c64, Plan64; fftw_plan_dft_r2c, fftw_execute_dft_r2c);
impl_r2c!(f32, c32, Plan32; fftwf_plan_dft_r2c, fftwf_execute_dft_r2c);

macro_rules! impl_c2r {
    ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl C2RPlan for Plan<$C, $R, $Plan> {
            type Real = $R;
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Complex],
                out: &mut [Self::Real],
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn c2r(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Real]) -> Result<()> {
                self.check(in_, out)?;
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_c2r!

impl_c2r!(f64, c64, Plan64; fftw_plan_dft_c2r, fftw_execute_dft_c2r);
impl_c2r!(f32, c32, Plan32; fftwf_plan_dft_c2r, fftwf_execute_dft_c2r);

macro_rules! impl_plan_spec {
    ($Plan:ty; $destroy_plan:ident, $print_plan:ident) => {
        impl PlanSpec for $Plan {
            fn validate(self) -> Result<Self> {
                if self.is_null() {
                    Err(Error::InvalidPlanError {})
                } else {
                    Ok(self)
                }
            }
            fn destroy(self) {
                excall!{ $destroy_plan(self) }
            }
            fn print(self) {
                excall!{ $print_plan(self) }
            }
        }
    };
} // impl_plan_spec!

impl_plan_spec!(Plan64; fftw_destroy_plan, fftw_print_plan);
impl_plan_spec!(Plan32; fftwf_destroy_plan, fftwf_print_plan);

// Convert [usize] -> [i32]
trait ToCInt {
    fn to_cint(&self) -> Vec<i32>;
}

impl ToCInt for [usize] {
    fn to_cint(&self) -> Vec<i32> {
        self.iter().map(|&x| x as i32).collect()
    }
}

fn slice_info<T>(a: &[T]) -> (usize, Alignment) {
    (a.len(), alignment_of(a))
}
<commit_msg>*Plan::aligned<commit_after>//! Plan in FFTW
//!
//! See also [Using Plans] in the original document
//! [Using Plans]: http://www.fftw.org/fftw3_doc/Using-Plans.html

use array::{alignment_of, AlignedAllocable, AlignedVec, Alignment};
use error::*;
use ffi::*;
use types::{c32, c64, Flag, Sign};

use std::marker::PhantomData;

pub type C2CPlan64 = Plan<c64, c64, Plan64>;
pub type C2CPlan32 = Plan<c32, c32, Plan32>;
pub type R2CPlan64 = Plan<f64, c64, Plan64>;
pub type R2CPlan32 = Plan<f32, c32, Plan32>;
pub type C2RPlan64 = Plan<c64, f64, Plan64>;
pub type C2RPlan32 = Plan<c32, f32, Plan32>;

/// Typed wrapper of `fftw_plan`
///
/// The plan in FFTW manages the contains all information necessary to compute the transform,
/// including the pointers to the input and output arrays.
/// However, this wrapper *does not modify* this pointer once after the plan is created
/// since it should be *unsafe* in terms of Rust.
/// Instead, this plan executes a transform for different arrays with [new-array execute functions]
/// with related associated functions, e.g. `C2CPlan::c2c`.
///
/// [new-array execute functions]: http://www.fftw.org/fftw3_doc/New_002darray-Execute-Functions.html
pub struct Plan<A, B, Plan: PlanSpec> {
    plan: Plan,
    input: (usize, Alignment),
    output: (usize, Alignment),
    phantom: PhantomData<(A, B)>,
}

impl<A, B, P: PlanSpec> Drop for Plan<A, B, P> {
    fn drop(&mut self) {
        self.plan.destroy();
    }
}

impl<A, B, P: PlanSpec> Plan<A, B, P> {
    fn check(&self, in_: &[A], out: &[B]) -> Result<()> {
        if self.input != slice_info(in_) {
            return Err(Error::InputArrayMismatch {
                expect: self.input,
                actual: slice_info(in_),
            });
        }
        if self.output != slice_info(out) {
            return Err(Error::OutputArrayMismatch {
                expect: self.output,
                actual: slice_info(out),
            });
        }
        Ok(())
    }
}

/// Trait for Plan makers
pub trait PlanSpec: Clone + Copy {
    fn validate(self) -> Result<Self>;
    fn destroy(self);
    fn print(self);
}

/// Marker for 64-bit floating point FFT
pub type Plan64 = fftw_plan;
/// Marker for 32-bit floating point FFT
pub type Plan32 = fftwf_plan;

/// Trait for the plan of Complex-to-Complex transformation
pub trait C2CPlan: Sized {
    type Complex: AlignedAllocable;

    /// Create new plan with aligned vector
    fn aligned(shape: &[usize], sign: Sign, flag: Flag) -> Result<Self> {
        let n: usize = shape.iter().product();
        let mut in_ = AlignedVec::new(n);
        let mut out = AlignedVec::new(n);
        Self::new(shape, &mut in_, &mut out, sign, flag)
    }

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-complex transform
    fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()>;
}

/// Trait for the plan of Real-to-Complex transformation
pub trait R2CPlan: Sized {
    type Real : AlignedAllocable;
    type Complex: AlignedAllocable;

    /// Create new plan with aligned vector
    fn aligned(shape: &[usize], flag: Flag) -> Result<Self> {
        let n: usize = shape.iter().product();
        let mut in_ = AlignedVec::new(n);
        let mut out = AlignedVec::new(n);
        Self::new(shape, &mut in_, &mut out, flag)
    }

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Real],
        out: &mut [Self::Complex],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute real-to-complex transform
    fn r2c(&mut self, in_: &mut [Self::Real], out: &mut [Self::Complex]) -> Result<()>;
}

/// Trait for the plan of Complex-to-Real transformation
pub trait C2RPlan: Sized {
    type Real: AlignedAllocable;
    type Complex: AlignedAllocable;

    /// Create new plan with aligned vector
    fn aligned(shape: &[usize], flag: Flag) -> Result<Self> {
        let n: usize = shape.iter().product();
        let mut in_ = AlignedVec::new(n);
        let mut out = AlignedVec::new(n);
        Self::new(shape, &mut in_, &mut out, flag)
    }

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Real],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-real transform
    fn c2r(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Real]) -> Result<()>;
}

macro_rules! impl_c2c {
    ($C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl C2CPlan for Plan<$C, $C, $Plan> {
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Complex],
                out: &mut [Self::Complex],
                sign: Sign,
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    sign as i32, flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()> {
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_c2c!

impl_c2c!(c64, Plan64; fftw_plan_dft, fftw_execute_dft);
impl_c2c!(c32, Plan32; fftwf_plan_dft, fftwf_execute_dft);

macro_rules! impl_r2c {
    ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl R2CPlan for Plan<$R, $C, $Plan> {
            type Real = $R;
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Real],
                out: &mut [Self::Complex],
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn r2c(&mut self, in_: &mut [Self::Real], out: &mut [Self::Complex]) -> Result<()> {
                self.check(in_, out)?;
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_r2c!

impl_r2c!(f64, c64, Plan64; fftw_plan_dft_r2c, fftw_execute_dft_r2c);
impl_r2c!(f32, c32, Plan32; fftwf_plan_dft_r2c, fftwf_execute_dft_r2c);

macro_rules! impl_c2r {
    ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
        impl C2RPlan for Plan<$C, $R, $Plan> {
            type Real = $R;
            type Complex = $C;
            fn new(
                shape: &[usize],
                in_: &mut [Self::Complex],
                out: &mut [Self::Real],
                flag: Flag,
            ) -> Result<Self> {
                let plan = excall!{ $plan(
                    shape.len() as i32,
                    shape.to_cint().as_mut_ptr() as *mut _,
                    in_.as_mut_ptr(),
                    out.as_mut_ptr(),
                    flag.bits())
                }.validate()?;
                Ok(Self {
                    plan,
                    input: slice_info(in_),
                    output: slice_info(out),
                    phantom: PhantomData,
                })
            }
            fn c2r(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Real]) -> Result<()> {
                self.check(in_, out)?;
                unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
                Ok(())
            }
        }
    };
} // impl_c2r!

impl_c2r!(f64, c64, Plan64; fftw_plan_dft_c2r, fftw_execute_dft_c2r);
impl_c2r!(f32, c32, Plan32; fftwf_plan_dft_c2r, fftwf_execute_dft_c2r);

macro_rules! impl_plan_spec {
    ($Plan:ty; $destroy_plan:ident, $print_plan:ident) => {
        impl PlanSpec for $Plan {
            fn validate(self) -> Result<Self> {
                if self.is_null() {
                    Err(Error::InvalidPlanError {})
                } else {
                    Ok(self)
                }
            }
            fn destroy(self) {
                excall!{ $destroy_plan(self) }
            }
            fn print(self) {
                excall!{ $print_plan(self) }
            }
        }
    };
} // impl_plan_spec!

impl_plan_spec!(Plan64; fftw_destroy_plan, fftw_print_plan);
impl_plan_spec!(Plan32; fftwf_destroy_plan, fftwf_print_plan);

// Convert [usize] -> [i32]
trait ToCInt {
    fn to_cint(&self) -> Vec<i32>;
}

impl ToCInt for [usize] {
    fn to_cint(&self) -> Vec<i32> {
        self.iter().map(|&x| x as i32).collect()
    }
}

fn slice_info<T>(a: &[T]) -> (usize, Alignment) {
    (a.len(), alignment_of(a))
}
<|endoftext|>


<commit_before>//! A filter for cross-site request forgery (CSRF).
//!
//! This middleware is stateless and [based on request
//! headers](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers).
//!
//! By default requests are allowed only if one of these is true:
//!
//! * The request method is safe (`GET`, `HEAD`, `OPTIONS`). It is the
//!   applications responsibility to ensure these methods cannot be used to
//!   execute unwanted actions. Note that upgrade requests for websockets are
//!   also considered safe.
//! * The `Origin` header (added automatically by the browser) matches one
//!   of the allowed origins.
//! * There is no `Origin` header but the `Referer` header matches one of
//!   the allowed origins.
//!
//! Use [`CsrfFilterBuilder::allow_xhr()`](struct.CsrfFilterBuilder.html#method.allow_xhr)
//! if you want to allow requests with unsafe methods via
//! [CORS](../cors/struct.Cors.html).
//!
//! # Example
//!
//! ```
//! # extern crate actix_web;
//! # use actix_web::*;
//!
//! use actix_web::middleware::csrf;
//!
//! fn handle_post(_req: HttpRequest) -> &'static str {
//!     "This action should only be triggered with requests from the same site"
//! }
//!
//! fn main() {
//!     let app = Application::new()
//!         .middleware(
//!             csrf::CsrfFilter::build()
//!                 .allowed_origin("https://www.example.com")
//!                 .finish())
//!         .resource("/", |r| {
//!             r.method(Method::GET).f(|_| httpcodes::HttpOk);
//!             r.method(Method::POST).f(handle_post);
//!         })
//!         .finish();
//! }
//! ```
//!
//! In this example the entire application is protected from CSRF.

use std::borrow::Cow;
use std::collections::HashSet;

use bytes::Bytes;
use error::{Result, ResponseError};
use http::{HeaderMap, HttpTryFrom, Uri, header};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use httpmessage::HttpMessage;
use httpcodes::HttpForbidden;
use middleware::{Middleware, Started};

/// Potential cross-site request forgery detected.
#[derive(Debug, Fail)]
pub enum CsrfError {
    /// The HTTP request header `Origin` was required but not provided.
    #[fail(display="Origin header required")]
    MissingOrigin,
    /// The HTTP request header `Origin` could not be parsed correctly.
    #[fail(display="Could not parse Origin header")]
    BadOrigin,
    /// The cross-site request was denied.
    #[fail(display="Cross-site request denied")]
    CsrDenied,
}

impl ResponseError for CsrfError {
    fn error_response(&self) -> HttpResponse {
        HttpForbidden.build().body(self.to_string()).unwrap()
    }
}

fn uri_origin(uri: &Uri) -> Option<String> {
    match (uri.scheme_part(), uri.host(), uri.port()) {
        (Some(scheme), Some(host), Some(port)) => {
            Some(format!("{}://{}:{}", scheme, host, port))
        }
        (Some(scheme), Some(host), None) => {
            Some(format!("{}://{}", scheme, host))
        }
        _ => None
    }
}

fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
    headers.get(header::ORIGIN)
        .map(|origin| {
            origin
                .to_str()
                .map_err(|_| CsrfError::BadOrigin)
                .map(|o| o.into())
        })
        .or_else(|| {
            headers.get(header::REFERER)
                .map(|referer| {
                    Uri::try_from(Bytes::from(referer.as_bytes()))
                        .ok()
                        .as_ref()
                        .and_then(uri_origin)
                        .ok_or(CsrfError::BadOrigin)
                        .map(|o| o.into())
                })
        })
}

/// A middleware that filters cross-site requests.
pub struct CsrfFilter {
    origins: HashSet<String>,
    allow_xhr: bool,
    allow_missing_origin: bool,
    allow_upgrade: bool,
}

impl CsrfFilter {
    /// Start building a `CsrfFilter`.
    pub fn build() -> CsrfFilterBuilder {
        CsrfFilterBuilder {
            csrf: CsrfFilter {
                origins: HashSet::new(),
                allow_xhr: false,
                allow_missing_origin: false,
                allow_upgrade: false,
            }
        }
    }

    fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> {
        let is_upgrade = req.headers().contains_key(header::UPGRADE);
        let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);

        if is_safe || (self.allow_xhr && req.headers().contains_key("x-requested-with")) {
            Ok(())
        } else if let Some(header) = origin(req.headers()) {
            match header {
                Ok(ref origin) if self.origins.contains(origin.as_ref()) => Ok(()),
                Ok(_) => Err(CsrfError::CsrDenied),
                Err(err) => Err(err),
            }
        } else if self.allow_missing_origin {
            Ok(())
        } else {
            Err(CsrfError::MissingOrigin)
        }
    }
}

impl<S> Middleware<S> for CsrfFilter {
    fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
        self.validate(req)?;
        Ok(Started::Done)
    }
}

/// Used to build a `CsrfFilter`.
///
/// To construct a CSRF filter:
///
/// 1. Call [`CsrfFilter::build`](struct.CsrfFilter.html#method.build) to
///    start building.
/// 2. [Add](struct.CsrfFilterBuilder.html#method.allowed_origin) allowed
///    origins.
/// 3. Call [finish](struct.CsrfFilterBuilder.html#method.finish) to retrieve
///    the constructed filter.
///
/// # Example
///
/// ```
/// use actix_web::middleware::csrf;
///
/// let csrf = csrf::CsrfFilter::build()
///     .allowed_origin("https://www.example.com")
///     .finish();
/// ```
pub struct CsrfFilterBuilder {
    csrf: CsrfFilter,
}

impl CsrfFilterBuilder {
    /// Add an origin that is allowed to make requests. Will be verified
    /// against the `Origin` request header.
    pub fn allowed_origin(mut self, origin: &str) -> CsrfFilterBuilder {
        self.csrf.origins.insert(origin.to_owned());
        self
    }

    /// Allow all requests with an `X-Requested-With` header.
    ///
    /// A cross-site attacker should not be able to send requests with custom
    /// headers unless a CORS policy whitelists them. Therefore it should be
    /// safe to allow requests with an `X-Requested-With` header (added
    /// automatically by many JavaScript libraries).
    ///
    /// This is disabled by default, because in Safari it is possible to
    /// circumvent this using redirects and Flash.
    ///
    /// Use this method to enable more lax filtering.
    pub fn allow_xhr(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_xhr = true;
        self
    }

    /// Allow requests if the expected `Origin` header is missing (and
    /// there is no `Referer` to fall back on).
    ///
    /// The filter is conservative by default, but it should be safe to allow
    /// missing `Origin` headers because a cross-site attacker cannot prevent
    /// the browser from sending `Origin` on unsafe requests.
    pub fn allow_missing_origin(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_missing_origin = true;
        self
    }

    /// Allow cross-site upgrade requests (for example to open a WebSocket).
    pub fn allow_upgrade(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_upgrade = true;
        self
    }

    /// Finishes building the `CsrfFilter` instance.
    pub fn finish(self) -> CsrfFilter {
        self.csrf
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use http::Method;
    use test::TestRequest;

    #[test]
    fn test_safe() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::HEAD)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_csrf() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_err());
    }

    #[test]
    fn test_referer() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Referer", "https://www.example.com/some/path?query=param")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }
}
<commit_msg>add tests for csrf upgrade filter<commit_after>//! A filter for cross-site request forgery (CSRF).
//!
//! This middleware is stateless and [based on request
//! headers](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers).
//!
//! By default requests are allowed only if one of these is true:
//!
//! * The request method is safe (`GET`, `HEAD`, `OPTIONS`). It is the
//!   applications responsibility to ensure these methods cannot be used to
//!   execute unwanted actions. Note that upgrade requests for websockets are
//!   also considered safe.
//! * The `Origin` header (added automatically by the browser) matches one
//!   of the allowed origins.
//! * There is no `Origin` header but the `Referer` header matches one of
//!   the allowed origins.
//!
//! Use [`CsrfFilterBuilder::allow_xhr()`](struct.CsrfFilterBuilder.html#method.allow_xhr)
//! if you want to allow requests with unsafe methods via
//! [CORS](../cors/struct.Cors.html).
//!
//! # Example
//!
//! ```
//! # extern crate actix_web;
//! # use actix_web::*;
//!
//! use actix_web::middleware::csrf;
//!
//! fn handle_post(_req: HttpRequest) -> &'static str {
//!     "This action should only be triggered with requests from the same site"
//! }
//!
//! fn main() {
//!     let app = Application::new()
//!         .middleware(
//!             csrf::CsrfFilter::build()
//!                 .allowed_origin("https://www.example.com")
//!                 .finish())
//!         .resource("/", |r| {
//!             r.method(Method::GET).f(|_| httpcodes::HttpOk);
//!             r.method(Method::POST).f(handle_post);
//!         })
//!         .finish();
//! }
//! ```
//!
//! In this example the entire application is protected from CSRF.

use std::borrow::Cow;
use std::collections::HashSet;

use bytes::Bytes;
use error::{Result, ResponseError};
use http::{HeaderMap, HttpTryFrom, Uri, header};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use httpmessage::HttpMessage;
use httpcodes::HttpForbidden;
use middleware::{Middleware, Started};

/// Potential cross-site request forgery detected.
#[derive(Debug, Fail)]
pub enum CsrfError {
    /// The HTTP request header `Origin` was required but not provided.
    #[fail(display="Origin header required")]
    MissingOrigin,
    /// The HTTP request header `Origin` could not be parsed correctly.
    #[fail(display="Could not parse Origin header")]
    BadOrigin,
    /// The cross-site request was denied.
    #[fail(display="Cross-site request denied")]
    CsrDenied,
}

impl ResponseError for CsrfError {
    fn error_response(&self) -> HttpResponse {
        HttpForbidden.build().body(self.to_string()).unwrap()
    }
}

fn uri_origin(uri: &Uri) -> Option<String> {
    match (uri.scheme_part(), uri.host(), uri.port()) {
        (Some(scheme), Some(host), Some(port)) => {
            Some(format!("{}://{}:{}", scheme, host, port))
        }
        (Some(scheme), Some(host), None) => {
            Some(format!("{}://{}", scheme, host))
        }
        _ => None
    }
}

fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
    headers.get(header::ORIGIN)
        .map(|origin| {
            origin
                .to_str()
                .map_err(|_| CsrfError::BadOrigin)
                .map(|o| o.into())
        })
        .or_else(|| {
            headers.get(header::REFERER)
                .map(|referer| {
                    Uri::try_from(Bytes::from(referer.as_bytes()))
                        .ok()
                        .as_ref()
                        .and_then(uri_origin)
                        .ok_or(CsrfError::BadOrigin)
                        .map(|o| o.into())
                })
        })
}

/// A middleware that filters cross-site requests.
pub struct CsrfFilter {
    origins: HashSet<String>,
    allow_xhr: bool,
    allow_missing_origin: bool,
    allow_upgrade: bool,
}

impl CsrfFilter {
    /// Start building a `CsrfFilter`.
    pub fn build() -> CsrfFilterBuilder {
        CsrfFilterBuilder {
            csrf: CsrfFilter {
                origins: HashSet::new(),
                allow_xhr: false,
                allow_missing_origin: false,
                allow_upgrade: false,
            }
        }
    }

    fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> {
        let is_upgrade = req.headers().contains_key(header::UPGRADE);
        let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);

        if is_safe || (self.allow_xhr && req.headers().contains_key("x-requested-with")) {
            Ok(())
        } else if let Some(header) = origin(req.headers()) {
            match header {
                Ok(ref origin) if self.origins.contains(origin.as_ref()) => Ok(()),
                Ok(_) => Err(CsrfError::CsrDenied),
                Err(err) => Err(err),
            }
        } else if self.allow_missing_origin {
            Ok(())
        } else {
            Err(CsrfError::MissingOrigin)
        }
    }
}

impl<S> Middleware<S> for CsrfFilter {
    fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
        self.validate(req)?;
        Ok(Started::Done)
    }
}

/// Used to build a `CsrfFilter`.
///
/// To construct a CSRF filter:
///
/// 1. Call [`CsrfFilter::build`](struct.CsrfFilter.html#method.build) to
///    start building.
/// 2. [Add](struct.CsrfFilterBuilder.html#method.allowed_origin) allowed
///    origins.
/// 3. Call [finish](struct.CsrfFilterBuilder.html#method.finish) to retrieve
///    the constructed filter.
///
/// # Example
///
/// ```
/// use actix_web::middleware::csrf;
///
/// let csrf = csrf::CsrfFilter::build()
///     .allowed_origin("https://www.example.com")
///     .finish();
/// ```
pub struct CsrfFilterBuilder {
    csrf: CsrfFilter,
}

impl CsrfFilterBuilder {
    /// Add an origin that is allowed to make requests. Will be verified
    /// against the `Origin` request header.
    pub fn allowed_origin(mut self, origin: &str) -> CsrfFilterBuilder {
        self.csrf.origins.insert(origin.to_owned());
        self
    }

    /// Allow all requests with an `X-Requested-With` header.
    ///
    /// A cross-site attacker should not be able to send requests with custom
    /// headers unless a CORS policy whitelists them. Therefore it should be
    /// safe to allow requests with an `X-Requested-With` header (added
    /// automatically by many JavaScript libraries).
    ///
    /// This is disabled by default, because in Safari it is possible to
    /// circumvent this using redirects and Flash.
    ///
    /// Use this method to enable more lax filtering.
    pub fn allow_xhr(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_xhr = true;
        self
    }

    /// Allow requests if the expected `Origin` header is missing (and
    /// there is no `Referer` to fall back on).
    ///
    /// The filter is conservative by default, but it should be safe to allow
    /// missing `Origin` headers because a cross-site attacker cannot prevent
    /// the browser from sending `Origin` on unsafe requests.
    pub fn allow_missing_origin(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_missing_origin = true;
        self
    }

    /// Allow cross-site upgrade requests (for example to open a WebSocket).
    pub fn allow_upgrade(mut self) -> CsrfFilterBuilder {
        self.csrf.allow_upgrade = true;
        self
    }

    /// Finishes building the `CsrfFilter` instance.
    pub fn finish(self) -> CsrfFilter {
        self.csrf
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use http::Method;
    use test::TestRequest;

    #[test]
    fn test_safe() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::HEAD)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_csrf() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Origin", "https://www.w3.org")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_err());
    }

    #[test]
    fn test_referer() {
        let csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let mut req = TestRequest::with_header("Referer", "https://www.example.com/some/path?query=param")
            .method(Method::POST)
            .finish();

        assert!(csrf.start(&mut req).is_ok());
    }

    #[test]
    fn test_upgrade() {
        let strict_csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .finish();

        let lax_csrf = CsrfFilter::build()
            .allowed_origin("https://www.example.com")
            .allow_upgrade()
            .finish();

        let mut req = TestRequest::with_header("Origin", "https://cswsh.com")
            .header("Connection", "Upgrade")
            .header("Upgrade", "websocket")
            .method(Method::GET)
            .finish();

        assert!(strict_csrf.start(&mut req).is_err());
        assert!(lax_csrf.start(&mut req).is_ok());
    }
}
<|endoftext|>


<commit_before>//! A shared ordered log.

use std::rc::Rc;
use std::cell::RefCell;
use std::time::{Instant, Duration};
use std::collections::VecDeque;

use ::{communication::Allocate, ExchangeData, PartialOrder};
use ::scheduling::Scheduler;
use worker::Worker;
use dataflow::channels::pact::Exchange;
use dataflow::operators::generic::operator::source;
use dataflow::operators::generic::operator::Operator;
use scheduling::activate::Activator;

// A Sequencer needs all operators firing with high frequency, because
// it uses the timer to gauge progress. If other workers cease
// advancing their own capabilities, although they might receive a
// record they may not actually tick forward their own source clocks,
// and no one will actually form the sequence.
//
// A CatchupActivator is an activator with an optional timestamp
// attached. This allows us to represent a special state, where after
// receiving an action from another worker, each of the other workers
// will keep scheduling its source operator, until its capability
// timestamp exceeds the greatest timestamp that the sink has
// received.
//
// This allows operators to go quiet again until a new requests shows
// up. The operators lose the ability to confirm that nothing is
// scheduled for a particular time (they could request this with a
// no-op event bearing a timestamp), but everyone still sees the same
// sequence.
struct CatchupActivator {
    pub catchup_until: Option<Duration>,
    activator: Activator,
}

impl CatchupActivator {
    pub fn activate(&self) {
        self.activator.activate();
    }
}

/// Orders elements inserted across all workers.
///
/// A Sequencer allows each worker to insert into a consistent ordered
/// sequence that is seen by all workers in the same order.
pub struct Sequencer<T> {
    activator: Rc<RefCell<Option<CatchupActivator>>>,
    send: Rc<RefCell<VecDeque<T>>>, // proposed items.
    recv: Rc<RefCell<VecDeque<T>>>, // sequenced items.
}

impl<T: ExchangeData> Sequencer<T> {

    /// Creates a new Sequencer.
    ///
    /// The `timer` instant is used to synchronize the workers, who use this
    /// elapsed time as their timestamp. Elements are ordered by this time,
    /// and cannot be made visible until all workers have reached the time.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::time::{Instant, Duration};
    ///
    /// use timely::Configuration;
    /// use timely::synchronization::Sequencer;
    ///
    /// timely::execute(Configuration::Process(4), |worker| {
    ///     let timer = Instant::now();
    ///     let mut sequencer = Sequencer::new(worker, timer);
    ///
    ///     for round in 0 .. 10 {
    ///
    ///         // Sleep, and then send an announcement on wake-up.
    ///         std::thread::sleep(Duration::from_millis(1 + worker.index() as u64));
    ///         sequencer.push(format!("worker {:?}, round {:?}", worker.index(), round));
    ///
    ///         // Ensures the pushed string is sent.
    ///         worker.step();
    ///
    ///         // Read out received announcements.
    ///         while let Some(element) = sequencer.next() {
    ///             println!("{:?}:\tWorker {:?}:\t recv'd: {:?}", timer.elapsed(), worker.index(), element);
    ///         }
    ///     }
    /// }).expect("Timely computation did not complete correctly.");
    /// ```
    pub fn new<A: Allocate>(worker: &mut Worker<A>, timer: Instant) -> Self {

        let send: Rc<RefCell<VecDeque<T>>> = Rc::new(RefCell::new(VecDeque::new()));
        let recv = Rc::new(RefCell::new(VecDeque::new()));
        let send_weak = Rc::downgrade(&send);
        let recv_weak = Rc::downgrade(&recv);

        // The SequenceInput activator will be held by the sequencer,
        // by the operator itself, and by the sink operator. We can
        // only initialize the activator once we obtain the operator
        // address.
        let activator: Rc<RefCell<Option<CatchupActivator>>> = Rc::new(RefCell::new(None));
        let activator_source = activator.clone(); //Rc::downgrade(&activator);
        let activator_sink = activator.clone(); //Rc::downgrade(&activator);

        // build a dataflow used to serialize and circulate commands
        worker.dataflow::<Duration,_,_>(move |dataflow| {

            let scope = dataflow.clone();
            let peers = dataflow.peers();

            let mut recvd = Vec::new();
            let mut vector = Vec::new();

            // monotonic counter to maintain per-worker total order.
            let mut counter = 0;

            // a source that attempts to pull from `recv` and produce commands for everyone
            source(dataflow, "SequenceInput", move |capability, info| {

                // intialize activator, now that we have the address
                activator_source
                    .borrow_mut()
                    .replace(CatchupActivator {
                        activator: scope.activator_for(&info.address[..]),
                        catchup_until: None,
                    });

                // so we can drop, if input queue vanishes.
                let mut capability = Some(capability);

                // closure broadcasts any commands it grabs.
                move |output| {

                    if let Some(send_queue) = send_weak.upgrade() {

                        // capability *should* still be non-None.
                        let capability = capability.as_mut().expect("Capability unavailable");

                        // downgrade capability to current time.
                        capability.downgrade(&timer.elapsed());

                        // drain and broadcast `send`.
                        let mut session = output.session(&capability);
                        let mut borrow = send_queue.borrow_mut();
                        for element in borrow.drain(..) {
                            for worker_index in 0 .. peers {
                                session.give((worker_index, counter, element.clone()));
                            }
                            counter += 1;
                        }

                        let mut activator_borrow = activator_source.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        if let Some(t) = activator.catchup_until {
                            if capability.time().less_than(&t) {
                                activator.activate();
                            } else {
                                activator.catchup_until = None;
                            }
                        }
                    } else {
                        capability = None;
                    }
                }
            })
            .sink(
                Exchange::new(|x: &(usize, usize, T)| x.0 as u64),
                "SequenceOutput",
                move |input| {

                    // grab each command and queue it up
                    input.for_each(|time, data| {
                        data.swap(&mut vector);
                        for (worker, counter, element) in vector.drain(..) {
                            recvd.push(((time.time().clone(), worker, counter), element));
                        }
                    });

                    recvd.sort_by(|x,y| x.0.cmp(&y.0));

                    if let Some(last) = recvd.last() {
                        let mut activator_borrow = activator_sink.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        activator.catchup_until = Some((last.0).0);
                        activator.activate();
                    }

                    // determine how many (which) elements to read from `recvd`.
                    let count = recvd.iter().filter(|&((ref time, _, _), _)| !input.frontier().less_equal(time)).count();
                    let iter = recvd.drain(..count);

                    if let Some(recv_queue) = recv_weak.upgrade() {
                        recv_queue.borrow_mut().extend(iter.map(|(_,elem)| elem));
                    }
                }
            );
        });

        Sequencer { activator, send, recv, }
    }

    /// Adds an element to the shared log.
    pub fn push(&mut self, element: T) {
        self.send.borrow_mut().push_back(element);
        self.activator.borrow_mut().as_mut().unwrap().activate();
    }

    /// Reads the next element from the shared log.
    pub fn next(&mut self) -> Option<T> {
        self.recv.borrow_mut().pop_front()
    }
}
<commit_msg>tidy type annotations/comments<commit_after>//! A shared ordered log.

use std::rc::Rc;
use std::cell::RefCell;
use std::time::{Instant, Duration};
use std::collections::VecDeque;

use ::{communication::Allocate, ExchangeData, PartialOrder};
use ::scheduling::Scheduler;
use worker::Worker;
use dataflow::channels::pact::Exchange;
use dataflow::operators::generic::operator::source;
use dataflow::operators::generic::operator::Operator;
use scheduling::activate::Activator;

// A Sequencer needs all operators firing with high frequency, because
// it uses the timer to gauge progress. If other workers cease
// advancing their own capabilities, although they might receive a
// record they may not actually tick forward their own source clocks,
// and no one will actually form the sequence.
//
// A CatchupActivator is an activator with an optional timestamp
// attached. This allows us to represent a special state, where after
// receiving an action from another worker, each of the other workers
// will keep scheduling its source operator, until its capability
// timestamp exceeds the greatest timestamp that the sink has
// received.
//
// This allows operators to go quiet again until a new requests shows
// up. The operators lose the ability to confirm that nothing is
// scheduled for a particular time (they could request this with a
// no-op event bearing a timestamp), but everyone still sees the same
// sequence.
struct CatchupActivator {
    pub catchup_until: Option<Duration>,
    activator: Activator,
}

impl CatchupActivator {
    pub fn activate(&self) {
        self.activator.activate();
    }
}

/// Orders elements inserted across all workers.
///
/// A Sequencer allows each worker to insert into a consistent ordered
/// sequence that is seen by all workers in the same order.
pub struct Sequencer<T> {
    activator: Rc<RefCell<Option<CatchupActivator>>>,
    send: Rc<RefCell<VecDeque<T>>>, // proposed items.
    recv: Rc<RefCell<VecDeque<T>>>, // sequenced items.
}

impl<T: ExchangeData> Sequencer<T> {

    /// Creates a new Sequencer.
    ///
    /// The `timer` instant is used to synchronize the workers, who use this
    /// elapsed time as their timestamp. Elements are ordered by this time,
    /// and cannot be made visible until all workers have reached the time.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::time::{Instant, Duration};
    ///
    /// use timely::Configuration;
    /// use timely::synchronization::Sequencer;
    ///
    /// timely::execute(Configuration::Process(4), |worker| {
    ///     let timer = Instant::now();
    ///     let mut sequencer = Sequencer::new(worker, timer);
    ///
    ///     for round in 0 .. 10 {
    ///
    ///         // Sleep, and then send an announcement on wake-up.
    ///         std::thread::sleep(Duration::from_millis(1 + worker.index() as u64));
    ///         sequencer.push(format!("worker {:?}, round {:?}", worker.index(), round));
    ///
    ///         // Ensures the pushed string is sent.
    ///         worker.step();
    ///
    ///         // Read out received announcements.
    ///         while let Some(element) = sequencer.next() {
    ///             println!("{:?}:\tWorker {:?}:\t recv'd: {:?}", timer.elapsed(), worker.index(), element);
    ///         }
    ///     }
    /// }).expect("Timely computation did not complete correctly.");
    /// ```
    pub fn new<A: Allocate>(worker: &mut Worker<A>, timer: Instant) -> Self {

        let send: Rc<RefCell<VecDeque<T>>> = Rc::new(RefCell::new(VecDeque::new()));
        let recv = Rc::new(RefCell::new(VecDeque::new()));
        let send_weak = Rc::downgrade(&send);
        let recv_weak = Rc::downgrade(&recv);

        // The SequenceInput activator will be held by the sequencer,
        // by the operator itself, and by the sink operator. We can
        // only initialize the activator once we obtain the operator
        // address.
        let activator = Rc::new(RefCell::new(None));
        let activator_source = activator.clone();
        let activator_sink = activator.clone();

        // build a dataflow used to serialize and circulate commands
        worker.dataflow::<Duration,_,_>(move |dataflow| {

            let scope = dataflow.clone();
            let peers = dataflow.peers();

            let mut recvd = Vec::new();
            let mut vector = Vec::new();

            // monotonic counter to maintain per-worker total order.
            let mut counter = 0;

            // a source that attempts to pull from `recv` and produce commands for everyone
            source(dataflow, "SequenceInput", move |capability, info| {

                // intialize activator, now that we have the address
                activator_source
                    .borrow_mut()
                    .replace(CatchupActivator {
                        activator: scope.activator_for(&info.address[..]),
                        catchup_until: None,
                    });

                // so we can drop, if input queue vanishes.
                let mut capability = Some(capability);

                // closure broadcasts any commands it grabs.
                move |output| {

                    if let Some(send_queue) = send_weak.upgrade() {

                        // capability *should* still be non-None.
                        let capability = capability.as_mut().expect("Capability unavailable");

                        // downgrade capability to current time.
                        capability.downgrade(&timer.elapsed());

                        // drain and broadcast `send`.
                        let mut session = output.session(&capability);
                        let mut borrow = send_queue.borrow_mut();
                        for element in borrow.drain(..) {
                            for worker_index in 0 .. peers {
                                session.give((worker_index, counter, element.clone()));
                            }
                            counter += 1;
                        }

                        let mut activator_borrow = activator_source.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        if let Some(t) = activator.catchup_until {
                            if capability.time().less_than(&t) {
                                activator.activate();
                            } else {
                                activator.catchup_until = None;
                            }
                        }
                    } else {
                        capability = None;
                    }
                }
            })
            .sink(
                Exchange::new(|x: &(usize, usize, T)| x.0 as u64),
                "SequenceOutput",
                move |input| {

                    // grab each command and queue it up
                    input.for_each(|time, data| {
                        data.swap(&mut vector);
                        for (worker, counter, element) in vector.drain(..) {
                            recvd.push(((time.time().clone(), worker, counter), element));
                        }
                    });

                    recvd.sort_by(|x,y| x.0.cmp(&y.0));

                    if let Some(last) = recvd.last() {
                        let mut activator_borrow = activator_sink.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        activator.catchup_until = Some((last.0).0);
                        activator.activate();
                    }

                    // determine how many (which) elements to read from `recvd`.
                    let count = recvd.iter().filter(|&((ref time, _, _), _)| !input.frontier().less_equal(time)).count();
                    let iter = recvd.drain(..count);

                    if let Some(recv_queue) = recv_weak.upgrade() {
                        recv_queue.borrow_mut().extend(iter.map(|(_,elem)| elem));
                    }
                }
            );
        });

        Sequencer { activator, send, recv, }
    }

    /// Adds an element to the shared log.
    pub fn push(&mut self, element: T) {
        self.send.borrow_mut().push_back(element);
        self.activator.borrow_mut().as_mut().unwrap().activate();
    }

    /// Reads the next element from the shared log.
    pub fn next(&mut self) -> Option<T> {
        self.recv.borrow_mut().pop_front()
    }
}
<|endoftext|>


<commit_before>use chrono::NaiveDateTime;
use diesel::*;

use models::Crate;
use schema::*;
use views::EncodableCategory;

#[derive(Clone, Identifiable, Queryable, QueryableByName, Debug)]
#[table_name = "categories"]
pub struct Category {
    pub id: i32,
    pub category: String,
    pub slug: String,
    pub description: String,
    pub crates_cnt: i32,
    pub created_at: NaiveDateTime,
}

#[derive(Associations, Insertable, Identifiable, Debug, Clone, Copy)]
#[belongs_to(Category)]
#[belongs_to(Crate)]
#[table_name = "crates_categories"]
#[primary_key(crate_id, category_id)]
pub struct CrateCategory {
    crate_id: i32,
    category_id: i32,
}

impl Category {
    pub fn encodable(self) -> EncodableCategory {
        let Category {
            crates_cnt,
            category,
            slug,
            description,
            created_at,
            ..
        } = self;
        EncodableCategory {
            id: slug.clone(),
            slug: slug.clone(),
            description: description.clone(),
            created_at,
            crates_cnt,
            category,
        }
    }

    pub fn update_crate<'a>(
        conn: &PgConnection,
        krate: &Crate,
        slugs: &[&'a str],
    ) -> QueryResult<Vec<&'a str>> {
        use diesel::dsl::any;

        conn.transaction(|| {
            let categories = categories::table
                .filter(categories::slug.eq(any(slugs)))
                .load::<Category>(conn)?;
            let invalid_categories = slugs
                .iter()
                .cloned()
                .filter(|s| !categories.iter().any(|c| c.slug == *s))
                .collect();
            let crate_categories = categories
                .iter()
                .map(|c| CrateCategory {
                    category_id: c.id,
                    crate_id: krate.id,
                })
                .collect::<Vec<_>>();

            delete(CrateCategory::belonging_to(krate)).execute(conn)?;
            insert_into(crates_categories::table)
                .values(&crate_categories)
                .execute(conn)?;
            Ok(invalid_categories)
        })
    }

    pub fn count_toplevel(conn: &PgConnection) -> QueryResult<i64> {
        use self::categories::dsl::*;

        categories
            .filter(category.not_like("%::%"))
            .count()
            .get_result(conn)
    }

    pub fn toplevel(
        conn: &PgConnection,
        sort: &str,
        limit: i64,
        offset: i64,
    ) -> QueryResult<Vec<Category>> {
        use diesel::dsl::*;
        use diesel::select;

        let sort_sql = match sort {
            "crates" => "ORDER BY crates_cnt DESC",
            _ => "ORDER BY category ASC",
        };

        // Collect all the top-level categories and sum up the crates_cnt of
        // the crates in all subcategories
        select(sql::<categories::SqlType>(&format!(
            "c.id, c.category, c.slug, c.description,
                sum(c2.crates_cnt)::int as crates_cnt, c.created_at
             FROM categories as c
             INNER JOIN categories c2 ON split_part(c2.slug, '::', 1) = c.slug
             WHERE split_part(c.slug, '::', 1) = c.slug
             GROUP BY c.id
             {} LIMIT {} OFFSET {}",
            sort_sql, limit, offset
        ))).load(conn)
    }

    pub fn subcategories(&self, conn: &PgConnection) -> QueryResult<Vec<Category>> {
        use diesel::sql_types::Text;

        sql_query(
            "SELECT c.id, c.category, c.slug, c.description, \
             COALESCE (( \
             SELECT sum(c2.crates_cnt)::int \
             FROM categories as c2 \
             WHERE c2.slug = c.slug \
             OR c2.slug LIKE c.slug || '::%' \
             ), 0) as crates_cnt, c.created_at \
             FROM categories as c \
             WHERE c.category ILIKE $1 || '::%' \
             AND c.category NOT ILIKE $1 || '::%::%'",
        ).bind::<Text, _>(&self.category)
            .load(conn)
    }

    pub fn parent_categories(&self, conn: &PgConnection) -> QueryResult<Vec<Category>> {
        // Path from top-level Category to this Category, not including this Category.
        use diesel::expression::dsl::*;
        use diesel::types::Text;

        sql::<categories::SqlType>(
            "SELECT c.id, c.category, c.slug, c.description, \
            COALESCE(( \
            SELECT sum(c2.crates_cnt)::int from categories c2 \
            WHERE path <@ subltree(c.path, 0, 2)), 0) as crates_cnt, c.created_at \
            FROM categories c \
            WHERE c.path @> (select path from categories where slug = $1) \
            AND c.slug <> $1
            ORDER BY c.path",
        ).bind::<Text, _>(&self.slug)
            .load(conn)
    }
}

#[derive(Insertable, AsChangeset, Default, Debug)]
#[table_name = "categories"]
pub struct NewCategory<'a> {
    pub category: &'a str,
    pub slug: &'a str,
}

impl<'a> NewCategory<'a> {
    /// Inserts the category into the database, or updates an existing one.
    pub fn create_or_update(&self, conn: &PgConnection) -> QueryResult<Category> {
        use schema::categories::dsl::*;

        insert_into(categories)
            .values(self)
            .on_conflict(slug)
            .do_update()
            .set(self)
            .get_result(conn)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use diesel::connection::SimpleConnection;
    use dotenv::dotenv;
    use std::env;

    fn pg_connection() -> PgConnection {
        let _ = dotenv();
        let database_url =
            env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must be set to run tests");
        let conn = PgConnection::establish(&database_url).unwrap();
        // These tests deadlock if run concurrently
        conn.batch_execute("BEGIN; LOCK categories IN ACCESS EXCLUSIVE MODE")
            .unwrap();
        conn
    }

    #[test]
    fn category_toplevel_excludes_subcategories() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug) VALUES
            ('Cat 2', 'cat2'), ('Cat 1', 'cat1'), ('Cat 1::sub', 'cat1::sub')
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 1".to_string(), "Cat 2".to_string()];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_orders_by_crates_cnt_when_sort_given() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 0), ('Cat 2', 'cat2', 2), ('Cat 3', 'cat3', 1)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec![
            "Cat 2".to_string(),
            "Cat 3".to_string(),
            "Cat 1".to_string(),
        ];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_applies_limit_and_offset() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug) VALUES
            ('Cat 1', 'cat1'), ('Cat 2', 'cat2')
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "", 1, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 1".to_string()];
        assert_eq!(expected, categories);

        let categories = Category::toplevel(&conn, "", 1, 1)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 2".to_string()];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_includes_subcategories_in_crate_cnt() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 1), ('Cat 1::sub', 'cat1::sub', 2),
            ('Cat 2', 'cat2', 3), ('Cat 2::Sub 1', 'cat2::sub1', 4),
            ('Cat 2::Sub 2', 'cat2::sub2', 5), ('Cat 3', 'cat3', 6)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![
            ("Cat 2".to_string(), 12),
            ("Cat 3".to_string(), 6),
            ("Cat 1".to_string(), 3),
        ];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_applies_limit_and_offset_after_grouping() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 1), ('Cat 1::sub', 'cat1::sub', 2),
            ('Cat 2', 'cat2', 3), ('Cat 2::Sub 1', 'cat2::sub1', 4),
            ('Cat 2::Sub 2', 'cat2::sub2', 5), ('Cat 3', 'cat3', 6)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 2, 0)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![("Cat 2".to_string(), 12), ("Cat 3".to_string(), 6)];
        assert_eq!(expected, categories);

        let categories = Category::toplevel(&conn, "crates", 2, 1)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![("Cat 3".to_string(), 6), ("Cat 1".to_string(), 3)];
        assert_eq!(expected, categories);
    }
}
<commit_msg>Add test and doc function for parent_categories<commit_after>use chrono::NaiveDateTime;
use diesel::*;

use models::Crate;
use schema::*;
use views::EncodableCategory;

#[derive(Clone, Identifiable, Queryable, QueryableByName, Debug)]
#[table_name = "categories"]
pub struct Category {
    pub id: i32,
    pub category: String,
    pub slug: String,
    pub description: String,
    pub crates_cnt: i32,
    pub created_at: NaiveDateTime,
}

#[derive(Associations, Insertable, Identifiable, Debug, Clone, Copy)]
#[belongs_to(Category)]
#[belongs_to(Crate)]
#[table_name = "crates_categories"]
#[primary_key(crate_id, category_id)]
pub struct CrateCategory {
    crate_id: i32,
    category_id: i32,
}

impl Category {
    pub fn encodable(self) -> EncodableCategory {
        let Category {
            crates_cnt,
            category,
            slug,
            description,
            created_at,
            ..
        } = self;
        EncodableCategory {
            id: slug.clone(),
            slug: slug.clone(),
            description: description.clone(),
            created_at,
            crates_cnt,
            category,
        }
    }

    pub fn update_crate<'a>(
        conn: &PgConnection,
        krate: &Crate,
        slugs: &[&'a str],
    ) -> QueryResult<Vec<&'a str>> {
        use diesel::dsl::any;

        conn.transaction(|| {
            let categories = categories::table
                .filter(categories::slug.eq(any(slugs)))
                .load::<Category>(conn)?;
            let invalid_categories = slugs
                .iter()
                .cloned()
                .filter(|s| !categories.iter().any(|c| c.slug == *s))
                .collect();
            let crate_categories = categories
                .iter()
                .map(|c| CrateCategory {
                    category_id: c.id,
                    crate_id: krate.id,
                })
                .collect::<Vec<_>>();

            delete(CrateCategory::belonging_to(krate)).execute(conn)?;
            insert_into(crates_categories::table)
                .values(&crate_categories)
                .execute(conn)?;
            Ok(invalid_categories)
        })
    }

    pub fn count_toplevel(conn: &PgConnection) -> QueryResult<i64> {
        use self::categories::dsl::*;

        categories
            .filter(category.not_like("%::%"))
            .count()
            .get_result(conn)
    }

    pub fn toplevel(
        conn: &PgConnection,
        sort: &str,
        limit: i64,
        offset: i64,
    ) -> QueryResult<Vec<Category>> {
        use diesel::dsl::*;
        use diesel::select;

        let sort_sql = match sort {
            "crates" => "ORDER BY crates_cnt DESC",
            _ => "ORDER BY category ASC",
        };

        // Collect all the top-level categories and sum up the crates_cnt of
        // the crates in all subcategories
        select(sql::<categories::SqlType>(&format!(
            "c.id, c.category, c.slug, c.description,
                sum(c2.crates_cnt)::int as crates_cnt, c.created_at
             FROM categories as c
             INNER JOIN categories c2 ON split_part(c2.slug, '::', 1) = c.slug
             WHERE split_part(c.slug, '::', 1) = c.slug
             GROUP BY c.id
             {} LIMIT {} OFFSET {}",
            sort_sql, limit, offset
        ))).load(conn)
    }

    pub fn subcategories(&self, conn: &PgConnection) -> QueryResult<Vec<Category>> {
        use diesel::sql_types::Text;

        sql_query(
            "SELECT c.id, c.category, c.slug, c.description, \
             COALESCE (( \
             SELECT sum(c2.crates_cnt)::int \
             FROM categories as c2 \
             WHERE c2.slug = c.slug \
             OR c2.slug LIKE c.slug || '::%' \
             ), 0) as crates_cnt, c.created_at \
             FROM categories as c \
             WHERE c.category ILIKE $1 || '::%' \
             AND c.category NOT ILIKE $1 || '::%::%'",
        ).bind::<Text, _>(&self.category)
            .load(conn)
    }

    /// Gathers the parent categories from the top-level Category to the direct parent of this Category.
    /// Returns categories as a Vector in order of traversal, not including this Category.
    /// The intention is to be able to have slugs or parent categories arrayed in order, to
    /// offer the frontend, for examples, slugs to create links to each parent category in turn.
    pub fn parent_categories(&self, conn: &PgConnection) -> QueryResult<Vec<Category>> {
        use diesel::expression::dsl::*;
        use diesel::types::Text;

        sql::<categories::SqlType>(
            "SELECT c.id, c.category, c.slug, c.description, \
            COALESCE(( \
            SELECT sum(c2.crates_cnt)::int from categories c2 \
            WHERE path <@ subltree(c.path, 0, 2)), 0) as crates_cnt, c.created_at \
            FROM categories c \
            WHERE c.path @> (select path from categories where slug = $1) \
            AND c.slug <> $1
            ORDER BY c.path",
        ).bind::<Text, _>(&self.slug)
            .load(conn)
    }
}

#[derive(Insertable, AsChangeset, Default, Debug)]
#[table_name = "categories"]
pub struct NewCategory<'a> {
    pub category: &'a str,
    pub slug: &'a str,
}

impl<'a> NewCategory<'a> {
    /// Inserts the category into the database, or updates an existing one.
    pub fn create_or_update(&self, conn: &PgConnection) -> QueryResult<Category> {
        use schema::categories::dsl::*;

        insert_into(categories)
            .values(self)
            .on_conflict(slug)
            .do_update()
            .set(self)
            .get_result(conn)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use diesel::connection::SimpleConnection;
    use dotenv::dotenv;
    use std::env;

    fn pg_connection() -> PgConnection {
        let _ = dotenv();
        let database_url =
            env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must be set to run tests");
        let conn = PgConnection::establish(&database_url).unwrap();
        // These tests deadlock if run concurrently
        conn.batch_execute("BEGIN; LOCK categories IN ACCESS EXCLUSIVE MODE")
            .unwrap();
        conn
    }

    #[test]
    fn category_toplevel_excludes_subcategories() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug) VALUES
            ('Cat 2', 'cat2'), ('Cat 1', 'cat1'), ('Cat 1::sub', 'cat1::sub')
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 1".to_string(), "Cat 2".to_string()];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_orders_by_crates_cnt_when_sort_given() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 0), ('Cat 2', 'cat2', 2), ('Cat 3', 'cat3', 1)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec![
            "Cat 2".to_string(),
            "Cat 3".to_string(),
            "Cat 1".to_string(),
        ];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_applies_limit_and_offset() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug) VALUES
            ('Cat 1', 'cat1'), ('Cat 2', 'cat2')
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "", 1, 0)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 1".to_string()];
        assert_eq!(expected, categories);

        let categories = Category::toplevel(&conn, "", 1, 1)
            .unwrap()
            .into_iter()
            .map(|c| c.category)
            .collect::<Vec<_>>();
        let expected = vec!["Cat 2".to_string()];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_includes_subcategories_in_crate_cnt() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 1), ('Cat 1::sub', 'cat1::sub', 2),
            ('Cat 2', 'cat2', 3), ('Cat 2::Sub 1', 'cat2::sub1', 4),
            ('Cat 2::Sub 2', 'cat2::sub2', 5), ('Cat 3', 'cat3', 6)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 10, 0)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![
            ("Cat 2".to_string(), 12),
            ("Cat 3".to_string(), 6),
            ("Cat 1".to_string(), 3),
        ];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_toplevel_applies_limit_and_offset_after_grouping() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 1), ('Cat 1::sub', 'cat1::sub', 2),
            ('Cat 2', 'cat2', 3), ('Cat 2::Sub 1', 'cat2::sub1', 4),
            ('Cat 2::Sub 2', 'cat2::sub2', 5), ('Cat 3', 'cat3', 6)
            ",
        ).unwrap();

        let categories = Category::toplevel(&conn, "crates", 2, 0)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![("Cat 2".to_string(), 12), ("Cat 3".to_string(), 6)];
        assert_eq!(expected, categories);

        let categories = Category::toplevel(&conn, "crates", 2, 1)
            .unwrap()
            .into_iter()
            .map(|c| (c.category, c.crates_cnt))
            .collect::<Vec<_>>();
        let expected = vec![("Cat 3".to_string(), 6), ("Cat 1".to_string(), 3)];
        assert_eq!(expected, categories);
    }

    #[test]
    fn category_parent_categories_includes_path_to_node_with_count() {
        let conn = pg_connection();
        conn.batch_execute(
            "INSERT INTO categories (category, slug, crates_cnt) VALUES
            ('Cat 1', 'cat1', 1), ('Cat 1::sub1', 'cat1::sub1', 2),
            ('Cat 1::sub2', 'cat1::sub2', 2), ('Cat 1::sub1::subsub1', 'cat1::sub1::subsub1', 2),
            ('Cat 2', 'cat2', 3), ('Cat 2::Sub 1', 'cat2::sub1', 4),
            ('Cat 2::Sub 2', 'cat2::sub2', 5), ('Cat 3', 'cat3', 200)
            ",
        ).unwrap();

        let cat = categories::table
            .filter(categories::slug.eq("cat1::sub1"))
            .first::<Category>(&conn)
            .unwrap();
        let subcats = cat.subcategories(&conn).unwrap();
        let parents = cat.parent_categories(&conn).unwrap();

        assert_eq!(parents.len(), 1);
        assert_eq!(parents[0].slug, "cat1");
        assert_eq!(parents[0].crates_cnt, 7);
        assert_eq!(subcats.len(), 1);
        assert_eq!(subcats[0].slug, "cat1::sub1::subsub1");
    }
}
<|endoftext|>