<commit_before>use argvalues::ArgValues;
use error;
use loader;
use download;

use gfx;
use glutin;
use gfx_window_glutin;

use gfx::traits::FactoryExt;
use gfx::Device;

use glutin::{VirtualKeyCode, ElementState, MouseButton, Event};

use std::time::Instant;

pub enum TextureId {
    ZERO,
    ONE,
    TWO,
    THREE,
}

type ColorFormat = gfx::format::Rgba8;
type DepthFormat = gfx::format::DepthStencil;

gfx_defines! {
    vertex Vertex {
        pos: [f32; 2] = "position",
    }

    pipeline pipe {
        // Vertex buffer
        vbuf: gfx::VertexBuffer<Vertex> = (),

        // Uniforms
        i_global_time: gfx::Global<f32> = "iGlobalTime",
        i_time: gfx::Global<f32> = "iTime",
        i_resolution: gfx::Global<[f32; 3]> = "iResolution",
        i_mouse: gfx::Global<[f32; 4]> = "iMouse",
        i_frame: gfx::Global<i32> = "iFrame",
        i_channel0: gfx::TextureSampler<[f32; 4]> = "iChannel0",
        i_channel1: gfx::TextureSampler<[f32; 4]> = "iChannel1",
        i_channel2: gfx::TextureSampler<[f32; 4]> = "iChannel2",
        i_channel3: gfx::TextureSampler<[f32; 4]> = "iChannel3",

        // Output color
        frag_color: gfx::BlendTarget<ColorFormat> = ("fragColor", gfx::state::MASK_ALL, gfx::preset::blend::ALPHA),
    }
}

const SCREEN: [Vertex; 4] = [
    Vertex{pos: [ 1.0,  1.0]}, // Top right
    Vertex{pos: [-1.0,  1.0]}, // Top left
    Vertex{pos: [-1.0, -1.0]}, // Bottom left
    Vertex{pos: [ 1.0, -1.0]}, // Bottom right
];

const SCREEN_INDICES: [u16; 6] = [
    0, 1, 2,
    0, 2, 3,
];

const CLEAR_COLOR: [f32; 4] = [1.0; 4];

pub fn run(av: &ArgValues) -> error::Result<()> {
    let (mut width, mut height) = (av.width, av.height);

    // Load vertex and fragment shaders into byte buffers
    let vert_src_buf = loader::load_vertex_shader();
    let frag_src_buf = match av.getid {
        Some(ref id) => {
            let (_, shadercode) = download::download(id)?;
            if av.andrun {
                loader::format_shader_src(&shadercode)
            } else {
                loader::load_fragment_shader(&av)?
            }
        },
        None => {
            loader::load_fragment_shader(&av)?
        }
    };
    let (vert_src_buf, frag_src_buf) = (vert_src_buf.as_slice(), frag_src_buf.as_slice());

    let builder = glutin::WindowBuilder::new()
        .with_title("shadertoy-rs")
        .with_dimensions(width as u32, height as u32)
        .with_vsync();

    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);

    let mut encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

    let mut pipeline = factory.create_pipeline_simple(vert_src_buf, frag_src_buf, pipe::new())?;

    let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&SCREEN, &SCREEN_INDICES[..]);

    // Load textures
    let texture0 = loader::load_texture(TextureId::ZERO, &av.texture0path, &mut factory)?;
    let texture1 = loader::load_texture(TextureId::ONE, &av.texture1path, &mut factory)?;
    let texture2 = loader::load_texture(TextureId::TWO, &av.texture2path, &mut factory)?;
    let texture3 = loader::load_texture(TextureId::THREE, &av.texture3path, &mut factory)?;

    let sampler = factory.create_sampler_linear();

    let mut data = pipe::Data {
        vbuf: vertex_buffer,

        i_global_time: 0.0,
        i_time: 0.0,
        i_resolution: [width, height, width/height],
        i_mouse: [0.0; 4],
        i_frame: -1,

        i_channel0: (texture0, sampler.clone()),
        i_channel1: (texture1, sampler.clone()),
        i_channel2: (texture2, sampler.clone()),
        i_channel3: (texture3, sampler.clone()),

        frag_color: main_color,
    };

    let mut last_mouse = ElementState::Released;
    let mut current_mouse = ElementState::Released;

    let (mut mx, mut my) = (0.0, 0.0);

    let mut xyzw = [0.0; 4];

    let mut start_time = Instant::now();

    loop {
        for event in window.poll_events() {
            match event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) | Event::Closed => {
                    return Ok(());
                },

                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::F5)) => {
                    // Reload fragment shader into byte buffer
                    let frag_src_res = loader::load_fragment_shader(&av)?;
                    let frag_src_buf = frag_src_res.as_slice();

                    // Recreate pipeline
                    pipeline = factory.create_pipeline_simple(vert_src_buf, frag_src_buf, pipe::new())?;

                    // Reset uniforms
                    data.i_global_time = 0.0;
                    data.i_time = 0.0;
                    data.i_resolution = [width, height, width/height];
                    data.i_mouse = [0.0; 4];
                    data.i_frame = -1;

                    start_time = Instant::now();
                },

                Event::Resized(new_width, new_height) => {
                    gfx_window_glutin::update_views(&window, &mut data.frag_color, &mut main_depth);

                    width = new_width as f32;
                    height = new_height as f32;
                },

                Event::MouseMoved(x, y) => {
                    mx = x as f32;
                    my = height - y as f32; // Flip y-axis
                },

                Event::MouseInput(state, button) => {
                    last_mouse = current_mouse;
                    if state == ElementState::Pressed && button == MouseButton::Left {
                        current_mouse = ElementState::Pressed;
                    } else {
                        current_mouse = ElementState::Released;
                    }
                },

                _ => ()
            }
        }

        // Mouse
        if current_mouse == ElementState::Pressed {
            xyzw[0] = mx;
            xyzw[1] = my;
            if last_mouse == ElementState::Released {
                xyzw[2] = mx;
                xyzw[3] = my;
            }
        } else {
            xyzw[2] = 0.0;
            xyzw[3] = 0.0;
        }
        data.i_mouse = xyzw;

        // Elapsed time
        let elapsed = start_time.elapsed();
        let elapsed_ms = (elapsed.as_secs() * 1000) + (elapsed.subsec_nanos()/1000000) as u64;
        let elapsed_sec = (elapsed_ms as f32) / 1000.0;
        data.i_global_time = elapsed_sec;
        data.i_time = elapsed_sec;

        // Resolution
        data.i_resolution = [width, height, width/height];

        // Frame
        data.i_frame += 1;

        // Draw
        encoder.clear(&data.frag_color, CLEAR_COLOR);
        encoder.draw(&slice, &pipeline, &data);
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<commit_msg>Don't run default shader if downloading (with no --run flag)<commit_after>use argvalues::ArgValues;
use error;
use loader;
use download;

use gfx;
use glutin;
use gfx_window_glutin;

use gfx::traits::FactoryExt;
use gfx::Device;

use glutin::{VirtualKeyCode, ElementState, MouseButton, Event};

use std::time::Instant;

pub enum TextureId {
    ZERO,
    ONE,
    TWO,
    THREE,
}

type ColorFormat = gfx::format::Rgba8;
type DepthFormat = gfx::format::DepthStencil;

gfx_defines! {
    vertex Vertex {
        pos: [f32; 2] = "position",
    }

    pipeline pipe {
        // Vertex buffer
        vbuf: gfx::VertexBuffer<Vertex> = (),

        // Uniforms
        i_global_time: gfx::Global<f32> = "iGlobalTime",
        i_time: gfx::Global<f32> = "iTime",
        i_resolution: gfx::Global<[f32; 3]> = "iResolution",
        i_mouse: gfx::Global<[f32; 4]> = "iMouse",
        i_frame: gfx::Global<i32> = "iFrame",
        i_channel0: gfx::TextureSampler<[f32; 4]> = "iChannel0",
        i_channel1: gfx::TextureSampler<[f32; 4]> = "iChannel1",
        i_channel2: gfx::TextureSampler<[f32; 4]> = "iChannel2",
        i_channel3: gfx::TextureSampler<[f32; 4]> = "iChannel3",

        // Output color
        frag_color: gfx::BlendTarget<ColorFormat> = ("fragColor", gfx::state::MASK_ALL, gfx::preset::blend::ALPHA),
    }
}

const SCREEN: [Vertex; 4] = [
    Vertex{pos: [ 1.0,  1.0]}, // Top right
    Vertex{pos: [-1.0,  1.0]}, // Top left
    Vertex{pos: [-1.0, -1.0]}, // Bottom left
    Vertex{pos: [ 1.0, -1.0]}, // Bottom right
];

const SCREEN_INDICES: [u16; 6] = [
    0, 1, 2,
    0, 2, 3,
];

const CLEAR_COLOR: [f32; 4] = [1.0; 4];

pub fn run(av: &ArgValues) -> error::Result<()> {
    let (mut width, mut height) = (av.width, av.height);

    // Load vertex and fragment shaders into byte buffers
    let vert_src_buf = loader::load_vertex_shader();
    let frag_src_buf = match av.getid {
        Some(ref id) => {
            let (_, shadercode) = download::download(id)?;

            // Don't run default shader if downloading (with no --run flag)
            if av.getid.is_some() && !av.andrun {
                return Ok(());
            }

            if av.andrun {
                loader::format_shader_src(&shadercode)
            } else {
                loader::load_fragment_shader(&av)?
            }
        },
        None => {
            loader::load_fragment_shader(&av)?
        }
    };
    let (vert_src_buf, frag_src_buf) = (vert_src_buf.as_slice(), frag_src_buf.as_slice());

    let builder = glutin::WindowBuilder::new()
        .with_title("shadertoy-rs")
        .with_dimensions(width as u32, height as u32)
        .with_vsync();

    let (window, mut device, mut factory, main_color, mut main_depth) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);

    let mut encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

    let mut pipeline = factory.create_pipeline_simple(vert_src_buf, frag_src_buf, pipe::new())?;

    let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&SCREEN, &SCREEN_INDICES[..]);

    // Load textures
    let texture0 = loader::load_texture(TextureId::ZERO, &av.texture0path, &mut factory)?;
    let texture1 = loader::load_texture(TextureId::ONE, &av.texture1path, &mut factory)?;
    let texture2 = loader::load_texture(TextureId::TWO, &av.texture2path, &mut factory)?;
    let texture3 = loader::load_texture(TextureId::THREE, &av.texture3path, &mut factory)?;

    let sampler = factory.create_sampler_linear();

    let mut data = pipe::Data {
        vbuf: vertex_buffer,

        i_global_time: 0.0,
        i_time: 0.0,
        i_resolution: [width, height, width/height],
        i_mouse: [0.0; 4],
        i_frame: -1,

        i_channel0: (texture0, sampler.clone()),
        i_channel1: (texture1, sampler.clone()),
        i_channel2: (texture2, sampler.clone()),
        i_channel3: (texture3, sampler.clone()),

        frag_color: main_color,
    };

    let mut last_mouse = ElementState::Released;
    let mut current_mouse = ElementState::Released;

    let (mut mx, mut my) = (0.0, 0.0);

    let mut xyzw = [0.0; 4];

    let mut start_time = Instant::now();

    loop {
        for event in window.poll_events() {
            match event {
                Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) | Event::Closed => {
                    return Ok(());
                },

                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::F5)) => {
                    // Reload fragment shader into byte buffer
                    let frag_src_res = loader::load_fragment_shader(&av)?;
                    let frag_src_buf = frag_src_res.as_slice();

                    // Recreate pipeline
                    pipeline = factory.create_pipeline_simple(vert_src_buf, frag_src_buf, pipe::new())?;

                    // Reset uniforms
                    data.i_global_time = 0.0;
                    data.i_time = 0.0;
                    data.i_resolution = [width, height, width/height];
                    data.i_mouse = [0.0; 4];
                    data.i_frame = -1;

                    start_time = Instant::now();
                },

                Event::Resized(new_width, new_height) => {
                    gfx_window_glutin::update_views(&window, &mut data.frag_color, &mut main_depth);

                    width = new_width as f32;
                    height = new_height as f32;
                },

                Event::MouseMoved(x, y) => {
                    mx = x as f32;
                    my = height - y as f32; // Flip y-axis
                },

                Event::MouseInput(state, button) => {
                    last_mouse = current_mouse;
                    if state == ElementState::Pressed && button == MouseButton::Left {
                        current_mouse = ElementState::Pressed;
                    } else {
                        current_mouse = ElementState::Released;
                    }
                },

                _ => ()
            }
        }

        // Mouse
        if current_mouse == ElementState::Pressed {
            xyzw[0] = mx;
            xyzw[1] = my;
            if last_mouse == ElementState::Released {
                xyzw[2] = mx;
                xyzw[3] = my;
            }
        } else {
            xyzw[2] = 0.0;
            xyzw[3] = 0.0;
        }
        data.i_mouse = xyzw;

        // Elapsed time
        let elapsed = start_time.elapsed();
        let elapsed_ms = (elapsed.as_secs() * 1000) + (elapsed.subsec_nanos()/1000000) as u64;
        let elapsed_sec = (elapsed_ms as f32) / 1000.0;
        data.i_global_time = elapsed_sec;
        data.i_time = elapsed_sec;

        // Resolution
        data.i_resolution = [width, height, width/height];

        // Frame
        data.i_frame += 1;

        // Draw
        encoder.clear(&data.frag_color, CLEAR_COLOR);
        encoder.draw(&slice, &pipeline, &data);
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<|endoftext|>


<commit_before>use linear_map::LinearMap;
use regex::Regex;
use error::Error;
use query::Query;
use value::{Key, Value};

/// A schema detailing what the data received from the driver (or inserted
/// into the driver) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// There is no schema. No validations should occur. Does not represent the
  /// abscense of any value, only represents that a schema does not define the
  /// data structure at this point.
  None,
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// Whether or not the minimum value should be included when validating.
    /// Default is `false`.
    exclusive_minimum: bool,
    /// The maximum value the number can be.
    maximum: Option<f64>,
    /// Whether or not the maximum value should be included when validating.
    /// Default is `false`.
    exclusive_maximum: bool
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Box<Schema>,
    /// The minimum number of items in the array.
    min_items: Option<u64>,
    /// The maximum number of items in the array.
    max_items: Option<u64>
  },
  /// Represents any tuple of values.
  Tuple {
    /// Schemas which each tuple value (in the same place) must comply with.
    items: Vec<Schema>,
    /// Whether or not there can be more items in the tuple.
    additional_items: bool
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: LinearMap<Key, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<Key>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values. An enum is
  /// considered a primitive type as if it is a single value is a higher order
  /// type, no variation is allowed.
  Enum(Vec<Value>)
}

impl Schema {
  /// Validates a query that a user would like to make on the database by
  /// comparing it to the schema. Mostly checks that all properties described
  /// in the query are accessible according to the schema.
  pub fn validate_query(&self, query: &Query) -> Result<(), Error> {
    static NO_PRIMITIVE_HINT: &'static str = "Try not querying specific properties of a primitive like `null` or `boolean`.";
    lazy_static! { static ref INTEGER_RE: Regex = Regex::new(r"^\d+$").unwrap(); }

    match (self, query) {
      // No schema describes these values, its the wild west. Go crazy query.
      // `Schema::None` does not represent the absence of value, just the
      // absence of validation.
      (&Schema::None, _) => Ok(()),
      (&Schema::Null, &Query::Value) => Ok(()),
      (&Schema::Null, &Query::Object(_)) => Err(Error::validation("Cannot deeply query null.", NO_PRIMITIVE_HINT)),
      (&Schema::Boolean, &Query::Value) => Ok(()),
      (&Schema::Boolean, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a boolean.", NO_PRIMITIVE_HINT)),
      (&Schema::Number{..}, &Query::Value) => Ok(()),
      (&Schema::Number{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a number.", NO_PRIMITIVE_HINT)),
      (&Schema::String{..}, &Query::Value) => Ok(()),
      (&Schema::String{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a string.", NO_PRIMITIVE_HINT)),
      (&Schema::Array{..}, &Query::Value) => Ok(()),
      (&Schema::Array{ref items,..}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else {
            items.validate_query(&query_properties[key])
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Tuple{..}, &Query::Value) => Ok(()),
      (&Schema::Tuple{ref items, ref additional_items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else if let Some(item_schema) = items.get(key.parse::<usize>().unwrap()) {
            item_schema.validate_query(&query_properties[key])
          } else if !additional_items {
            Err(Error::validation(format!("Tuple has only {} values. Can’t query the index {}.", items.len(), key), format!("Query a key less than or equal to {}.", items.len() - 1)))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Object{..}, &Query::Value) => Ok(()),
      (&Schema::Object{ref properties, ref additional_properties,..}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if let Some(property_schema) = properties.get(key) {
            property_schema.validate_query(&query_properties[key])
          } else if !additional_properties {
            Err(Error::validation(format!("Cannot query object property \"{}\".", key), "Query an object property that is defined in the schema."))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Enum(_), &Query::Value) => Ok(()),
      (&Schema::Enum(_), &Query::Object(_)) => Err(Error::validation("Cannot deeply query an enum.", NO_PRIMITIVE_HINT))
    }
  }
}

#[cfg(test)]
mod tests {
  use linear_map::LinearMap;
  use schema::Schema;

  #[test]
  fn test_query_none() {
    assert_eq!(Schema::None.validate_query(&qvalue!()).is_ok(), true);
    assert_eq!(Schema::None.validate_query(&qobject!{
      "s@#f&/Ij)82h(;pa0]" => qvalue!(),
      "123" => qvalue!(),
      "hello" => qvalue!(),
      "nested" => qobject!{
        "yo" => qvalue!()
      }
    }).is_ok(), true);
  }

  #[test]
  fn test_query_primitive() {
    assert!(Schema::Null.validate_query(&qvalue!()).is_ok());
    let obj_query = qobject!{};
    Schema::Null.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Boolean.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Number{
      multiple_of: None,
      minimum: None,
      exclusive_minimum: false,
      maximum: None,
      exclusive_maximum: false
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::String{
      min_length: None,
      max_length: None,
      pattern: None
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Enum(vec![
      vbool!(true),
      vbool!(false)
    ]).validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
  }

  #[test]
  fn test_query_array() {
    let array_none = Schema::Array {
      items: Box::new(Schema::None),
      min_items: None,
      max_items: None
    };
    let array_bool = Schema::Array {
      items: Box::new(Schema::Boolean),
      min_items: None,
      max_items: None
    };
    assert!(array_none.validate_query(&qvalue!()).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qvalue!()
    }).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qobject!{}
    }).is_ok());
    assert!(array_bool.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!(),
      "50" => qvalue!(),
      "999999999999999" => qvalue!()
    }).is_ok());
    array_none.validate_query(&qobject!{
      "hello" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"hello\"");
    array_bool.validate_query(&qobject!{
      "1" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
  }

  #[test]
  fn test_query_tuple() {
    let nums = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: false
    };
    let nums_additional = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: true
    };
    let nums_and_object = Schema::Tuple{
      items: vec![
        Schema::Boolean,
        Schema::Object{
          properties: {
            let mut map = LinearMap::new();
            map.insert(String::from("hello"), Schema::Boolean);
            map
          },
          required: vec![],
          additional_properties: false
        },
        Schema::Boolean
      ],
      additional_items: false
    };
    assert!(nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "2" => qvalue!()
    }).is_ok());
    nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Tuple has only 3 values\. Can’t query the index 3\.");
    nums.validate_query(&qobject!{
      "asd" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"asd\"");
    nums.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qobject!{},
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(nums_additional.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "3" => qvalue!(),
      "99999999" => qvalue!()
    }).is_ok());
    assert!(nums_and_object.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qobject!{"hello" => qvalue!()},
      "2" => qvalue!()
    }).is_ok());
  }

  #[test]
  fn test_query_object() {
    let object = Schema::Object{
      properties: {
        let mut map1 = LinearMap::new();
        map1.insert(String::from("hello"), Schema::Boolean);
        map1.insert(String::from("world"), Schema::Boolean);
        map1.insert(String::from("5"), Schema::Boolean);
        map1.insert(String::from("goodbye"), Schema::Object{
          properties: {
            let mut map2 = LinearMap::new();
            map2.insert(String::from("hello"), Schema::Boolean);
            map2.insert(String::from("world"), Schema::Boolean);
            map2
          },
          required: vec![],
          additional_properties: false
        });
        map1
      },
      required: vec![],
      additional_properties: false
    };
    let object_additional = Schema::Object{
      properties: {
        let mut map = LinearMap::new();
        map.insert(String::from("hello"), Schema::Boolean);
        map.insert(String::from("world"), Schema::Boolean);
        map
      },
      required: vec![],
      additional_properties: true
    };
    assert!(object.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!()
    }).is_ok());
    object.validate_query(&qobject!{
      "hello" => qvalue!(),
      "moon" => qvalue!()
    }).unwrap_err().assert_message("Cannot query object property \"moon\".");
    object.validate_query(&qobject!{
      "hello" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qvalue!()
      }
    }).is_ok());
    object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qobject!{}
      }
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object_additional.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!(),
      "moon" => qvalue!()
    }).is_ok());
  }
}
<commit_msg>refactor: remove min/max items<commit_after>use linear_map::LinearMap;
use regex::Regex;
use error::Error;
use query::Query;
use value::{Key, Value};

lazy_static! {
  static ref INTEGER_RE: Regex = Regex::new(r"^\d+$").unwrap();
}

/// A schema detailing what the data received from the driver (or inserted
/// into the driver) should be. Inspired after [JSON Schema][1]. A reference
/// on JSON Schema type-specific validations used in this enum may be found
/// [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum Schema {
  /// There is no schema. No validations should occur. Does not represent the
  /// abscense of any value, only represents that a schema does not define the
  /// data structure at this point.
  None,
  /// Represents the absence of any value.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps in specifying
    /// integers if this value is `Some(1)` for example.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// Whether or not the minimum value should be included when validating.
    /// Default is `false`.
    exclusive_minimum: bool,
    /// The maximum value the number can be.
    maximum: Option<f64>,
    /// Whether or not the maximum value should be included when validating.
    /// Default is `false`.
    exclusive_maximum: bool
  },
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    items: Box<Schema>
  },
  /// Represents any tuple of values.
  Tuple {
    /// Schemas which each tuple value (in the same place) must comply with.
    items: Vec<Schema>,
    /// Whether or not there can be more items in the tuple.
    additional_items: bool
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object properties.
    properties: LinearMap<Key, Schema>,
    /// Properties that are required to be in the object.
    required: Vec<Key>,
    /// Whether or not there may be extra properties outside of the ones
    /// defined by the properties map.
    additional_properties: bool
  },
  /// Represents a value which *must* be one of the defined values. An enum is
  /// considered a primitive type as if it is a single value is a higher order
  /// type, no variation is allowed.
  Enum(Vec<Value>)
}

impl Schema {
  /// Validates a query that a user would like to make on the database by
  /// comparing it to the schema. Mostly checks that all properties described
  /// in the query are accessible according to the schema.
  pub fn validate_query(&self, query: &Query) -> Result<(), Error> {
    static NO_PRIMITIVE_HINT: &'static str = "Try not querying specific properties of a primitive like `null` or `boolean`.";
    match (self, query) {
      // No schema describes these values, its the wild west. Go crazy query.
      // `Schema::None` does not represent the absence of value, just the
      // absence of validation.
      (&Schema::None, _) => Ok(()),
      (&Schema::Null, &Query::Value) => Ok(()),
      (&Schema::Null, &Query::Object(_)) => Err(Error::validation("Cannot deeply query null.", NO_PRIMITIVE_HINT)),
      (&Schema::Boolean, &Query::Value) => Ok(()),
      (&Schema::Boolean, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a boolean.", NO_PRIMITIVE_HINT)),
      (&Schema::Number{..}, &Query::Value) => Ok(()),
      (&Schema::Number{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a number.", NO_PRIMITIVE_HINT)),
      (&Schema::String{..}, &Query::Value) => Ok(()),
      (&Schema::String{..}, &Query::Object(_)) => Err(Error::validation("Cannot deeply query a string.", NO_PRIMITIVE_HINT)),
      (&Schema::Array{..}, &Query::Value) => Ok(()),
      (&Schema::Array{ref items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else {
            items.validate_query(&query_properties[key])
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Tuple{..}, &Query::Value) => Ok(()),
      (&Schema::Tuple{ref items, ref additional_items}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if !INTEGER_RE.is_match(key) {
            Err(Error::validation(format!("Cannot query non-integer \"{}\" array property.", key), "Only query integer array keys like 1, 2, and 3."))
          } else if let Some(item_schema) = items.get(key.parse::<usize>().unwrap()) {
            item_schema.validate_query(&query_properties[key])
          } else if !additional_items {
            Err(Error::validation(format!("Tuple has only {} values. Can’t query the index {}.", items.len(), key), format!("Query a key less than or equal to {}.", items.len() - 1)))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Object{..}, &Query::Value) => Ok(()),
      (&Schema::Object{ref properties, ref additional_properties,..}, &Query::Object(ref query_properties)) => {
        match query_properties.keys().map(|key| {
          if let Some(property_schema) = properties.get(key) {
            property_schema.validate_query(&query_properties[key])
          } else if !additional_properties {
            Err(Error::validation(format!("Cannot query object property \"{}\".", key), "Query an object property that is defined in the schema."))
          } else {
            Ok(())
          }
        }).find(|r| r.is_err()) {
          None => Ok(()),
          Some(error) => error
        }
      },
      (&Schema::Enum(_), &Query::Value) => Ok(()),
      (&Schema::Enum(_), &Query::Object(_)) => Err(Error::validation("Cannot deeply query an enum.", NO_PRIMITIVE_HINT))
    }
  }
}

#[cfg(test)]
mod tests {
  use linear_map::LinearMap;
  use schema::Schema;

  #[test]
  fn test_query_none() {
    assert_eq!(Schema::None.validate_query(&qvalue!()).is_ok(), true);
    assert_eq!(Schema::None.validate_query(&qobject!{
      "s@#f&/Ij)82h(;pa0]" => qvalue!(),
      "123" => qvalue!(),
      "hello" => qvalue!(),
      "nested" => qobject!{
        "yo" => qvalue!()
      }
    }).is_ok(), true);
  }

  #[test]
  fn test_query_primitive() {
    assert!(Schema::Null.validate_query(&qvalue!()).is_ok());
    let obj_query = qobject!{};
    Schema::Null.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Boolean.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Number{
      multiple_of: None,
      minimum: None,
      exclusive_minimum: false,
      maximum: None,
      exclusive_maximum: false
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::String{
      min_length: None,
      max_length: None,
      pattern: None
    }.validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
    Schema::Enum(vec![
      vbool!(true),
      vbool!(false)
    ]).validate_query(&obj_query).unwrap_err().assert_message(r"deeply query");
  }

  #[test]
  fn test_query_array() {
    let array_none = Schema::Array {
      items: Box::new(Schema::None)
    };
    let array_bool = Schema::Array {
      items: Box::new(Schema::Boolean)
    };
    assert!(array_none.validate_query(&qvalue!()).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qvalue!()
    }).is_ok());
    assert!(array_none.validate_query(&qobject!{
      "1" => qobject!{}
    }).is_ok());
    assert!(array_bool.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!(),
      "50" => qvalue!(),
      "999999999999999" => qvalue!()
    }).is_ok());
    array_none.validate_query(&qobject!{
      "hello" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"hello\"");
    array_bool.validate_query(&qobject!{
      "1" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
  }

  #[test]
  fn test_query_tuple() {
    let nums = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: false
    };
    let nums_additional = Schema::Tuple{
      items: vec![Schema::Boolean, Schema::Boolean, Schema::Boolean],
      additional_items: true
    };
    let nums_and_object = Schema::Tuple{
      items: vec![
        Schema::Boolean,
        Schema::Object{
          properties: {
            let mut map = LinearMap::new();
            map.insert(String::from("hello"), Schema::Boolean);
            map
          },
          required: vec![],
          additional_properties: false
        },
        Schema::Boolean
      ],
      additional_items: false
    };
    assert!(nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "2" => qvalue!()
    }).is_ok());
    nums.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Tuple has only 3 values\. Can’t query the index 3\.");
    nums.validate_query(&qobject!{
      "asd" => qvalue!(),
      "1" => qvalue!(),
      "2" => qvalue!(),
      "3" => qvalue!()
    }).unwrap_err().assert_message("non-integer \"asd\"");
    nums.validate_query(&qobject!{
      "1" => qvalue!(),
      "2" => qobject!{},
      "3" => qvalue!()
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(nums_additional.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qvalue!(),
      "3" => qvalue!(),
      "99999999" => qvalue!()
    }).is_ok());
    assert!(nums_and_object.validate_query(&qobject!{
      "0" => qvalue!(),
      "1" => qobject!{"hello" => qvalue!()},
      "2" => qvalue!()
    }).is_ok());
  }

  #[test]
  fn test_query_object() {
    let object = Schema::Object{
      properties: {
        let mut map1 = LinearMap::new();
        map1.insert(String::from("hello"), Schema::Boolean);
        map1.insert(String::from("world"), Schema::Boolean);
        map1.insert(String::from("5"), Schema::Boolean);
        map1.insert(String::from("goodbye"), Schema::Object{
          properties: {
            let mut map2 = LinearMap::new();
            map2.insert(String::from("hello"), Schema::Boolean);
            map2.insert(String::from("world"), Schema::Boolean);
            map2
          },
          required: vec![],
          additional_properties: false
        });
        map1
      },
      required: vec![],
      additional_properties: false
    };
    let object_additional = Schema::Object{
      properties: {
        let mut map = LinearMap::new();
        map.insert(String::from("hello"), Schema::Boolean);
        map.insert(String::from("world"), Schema::Boolean);
        map
      },
      required: vec![],
      additional_properties: true
    };
    assert!(object.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!()
    }).is_ok());
    object.validate_query(&qobject!{
      "hello" => qvalue!(),
      "moon" => qvalue!()
    }).unwrap_err().assert_message("Cannot query object property \"moon\".");
    object.validate_query(&qobject!{
      "hello" => qobject!{}
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qvalue!()
      }
    }).is_ok());
    object.validate_query(&qobject!{
      "goodbye" => qobject!{
        "hello" => qobject!{}
      }
    }).unwrap_err().assert_message(r"Cannot deeply query a boolean\.");
    assert!(object_additional.validate_query(&qobject!{
      "world" => qvalue!(),
      "5" => qvalue!(),
      "goodbye" => qvalue!(),
      "moon" => qvalue!()
    }).is_ok());
  }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;
extern crate time;

use std::str::FromStr;
use std::fmt;
use rustc_serialize::base64::FromBase64;
use time::Timespec;
use std::ascii::AsciiExt;

#[derive(Debug)]
pub enum Error {
    UnsupportedScheme,
    SchemeParseError,
    MissingAttributes,
    UnknownAttribute,
    InvalidTimestamp,
    Base64DecodeError,
}

#[derive(Clone, PartialEq, Debug)]
pub struct HawkScheme {
    pub id: String,
    pub ts: Timespec,
    pub nonce: String,
    pub mac: Vec<u8>,
    pub ext: Option<String>,
    pub hash: Option<Vec<u8>>,
    pub app: Option<String>,
    pub dlg: Option<String>,
}

impl hyper::header::Scheme for HawkScheme {
    fn scheme() -> Option<&'static str> {
        Some("Hawk")
    }

    fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Hawk blah=foo") // TODO
    }
}

// Authorization: Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", ext="some-app-ext-data", mac="6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE="
//
// Hawk id="test-client", ts="1430002620", nonce="DG2EzX", ext="some-app-data", mac="CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=", hash="", app="", dlg=""

// Hawk id="test-client", // string
// ts="1430002620", // date
// nonce="DG2EzX", // string
// mac="CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=", // bytes
// ext="some-app-data", (optional) // string
// hash="", (optional) // bytes
// app="", (optional) // string
// dlg="" (optional)  // string
//

impl FromStr for HawkScheme {
    type Err = Error;
    fn from_str(s: &str) -> Result<HawkScheme, Error> {
        // Check that it starts with "HAWK " (Space not optional)
        if s.len() < 5 || !s[..5].eq_ignore_ascii_case("hawk ") {
            return Err(Error::UnsupportedScheme);
        }

        let mut p = &s[4..];
        println!("");
        println!("AUTH: '{}'", &p);
        println!("Length: {}", p.len());

        // Required attributes
        let mut id: Option<&str> = None;
        let mut ts: Option<Timespec> = None;
        let mut nonce: Option<&str> = None;
        let mut mac: Option<Vec<u8>> = None;
        // Optional attributes
        let mut hash: Option<Vec<u8>> = None;
        let mut ext: Option<&str> = None;
        let mut app: Option<&str> = None;
        let mut dlg: Option<&str> = None;

        while p.len() > 0 {
            // Skip whitespace and commas used as separators
            p = p.trim_left_matches(|c| {
                return c == ',' || char::is_whitespace(c);
            });
            // Find first '=' which delimits attribute name from value
            match p.find("=") {
                Some(v) => {
                    let attr = &p[..v].trim();
                    if p.len() < v + 1 {
                        return Err(Error::SchemeParseError);
                    }
                    p = (&p[v + 1..]).trim_left();
                    let mut end: Option<usize>;
                    if p.starts_with("\"") {
                        p = &p[1..];
                        // We have poor RFC 7235 compliance here as we ought to
                        // support backslash escaped characters, but hawk doesn't allow this
                        // we won't either.
                        end = p.find("\"");
                    } else {
                        // Parse tokens for better RFC 7235 compliance, not allowed by hawk
                        // and we shall not serialize to this format
                        end = p.find(|c| {
                            return match c {
                                '!' => false,
                                '#' => false,
                                '$' => false,
                                '%' => false,
                                '&' => false,
                                '\'' => false,
                                '*' => false,
                                '+' => false,
                                '-' => false,
                                '.' => false,
                                '^' => false,
                                '_' => false,
                                '`' => false,
                                '|' => false,
                                '~' => false,
                                _ => !char::is_alphanumeric(c) && !char::is_numeric(c),
                            };
                        });
                        end = match end {
                            Some(v) => Some(v),
                            None => Some(p.len()),
                        };
                    }
                    match end {
                        Some(v) => {
                            let val = &p[..v];
                            match *attr {
                                "id" => id = Some(val),
                                "ts" => {
                                    match i64::from_str(val) {
                                        Ok(sec) => ts = Some(Timespec::new(sec, 0)),
                                        Err(_) => return Err(Error::InvalidTimestamp),
                                    };
                                }
                                "mac" => {
                                    match val.from_base64() {
                                        Ok(v) => mac = Some(v),
                                        Err(_) => return Err(Error::Base64DecodeError),
                                    }
                                }
                                "nonce" => nonce = Some(val),
                                "ext" => ext = Some(val),
                                "hash" => {
                                    match val.from_base64() {
                                        Ok(v) => hash = Some(v),
                                        Err(_) => return Err(Error::Base64DecodeError),
                                    }
                                }
                                "app" => app = Some(val),
                                "dlg" => dlg = Some(val),
                                _ => return Err(Error::UnknownAttribute),
                            };
                            // Break if we are at end of string, otherwise skip separator
                            if p.len() < v + 1 {
                                break;
                            }
                            p = &p[v + 1..].trim_left();
                        }
                        None => return Err(Error::SchemeParseError),
                    }
                }
                None => return Err(Error::SchemeParseError),
            };
        }

        return match (id, ts, nonce, mac) {
            (Some(id), Some(ts), Some(nonce), Some(mac)) => {
                Ok(HawkScheme {
                    id: id.to_string(),
                    ts: ts,
                    nonce: nonce.to_string(),
                    mac: mac,
                    ext: match ext {
                        Some(ext) => Some(ext.to_string()),
                        None => None,
                    },
                    hash: hash,
                    app: match app {
                        Some(app) => Some(app.to_string()),
                        None => None,
                    },
                    dlg: match dlg {
                        Some(dlg) => Some(dlg.to_string()),
                        None => None,
                    },
                })
            }
            _ => Err(Error::MissingAttributes),
        };
    }
}

#[test]
fn scheme_from_string() {
    let s = HawkScheme::from_str("Hawk , id  =  \"dh37fgj492je\", ts=\"1353832234\", \
                                  nonce=\"j4h3g2\"  , , ext=\"some-app-ext-data\", \
                                  mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"");
    assert!(s.is_ok(), "Parse failed!");
}

// TODO: more tests of from_str

// #[test]
// fn scheme_from_string_again() {
// let s = HawkScheme::from_str(
// "Hawk \
// id=\"dh37fgj492je\", \
// ts=\"1353832234\", \
// nonce=\"j4h3g2\", \
// ext=\"some-app-ext-data\", \
// mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
// hash=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
// app=\"my-app\", \
// dlg=\"my-authority\""
// );
// assert!(s.is_ok(), "Parse failed!");
// }
//
//
<commit_msg>support encoding and decoding Authorization: Hawk headers<commit_after>extern crate hyper;
extern crate rustc_serialize;
extern crate time;

use std::str::FromStr;
use std::fmt;
use rustc_serialize::base64::{FromBase64, ToBase64};
use rustc_serialize::base64;
use time::Timespec;
use std::ascii::AsciiExt;
use hyper::header::Scheme;

#[derive(Debug)]
pub enum Error {
    UnsupportedScheme,
    SchemeParseError,
    MissingAttributes,
    UnknownAttribute,
    InvalidTimestamp,
    Base64DecodeError,
}

#[derive(Clone, PartialEq, Debug)]
pub struct HawkScheme {
    pub id: String,
    pub ts: Timespec,
    pub nonce: String,
    pub mac: Vec<u8>,
    pub ext: Option<String>,
    pub hash: Option<Vec<u8>>,
    pub app: Option<String>,
    pub dlg: Option<String>,
}

impl Scheme for HawkScheme {
    fn scheme() -> Option<&'static str> {
        Some("Hawk")
    }

    fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let base64_config = base64::Config {
            char_set: base64::CharacterSet::Standard,
            newline: base64::Newline::LF,
            pad: true,
            line_length: None,
        };
        try!(write!(f,
                    "Hawk id=\"{}\", ts={}, nonce=\"{}\", mac=\"{}\"",
                    self.id,
                    self.ts.sec,
                    self.nonce,
                    self.mac.to_base64(base64_config),
                    ));
        if let Some(ref ext) = self.ext {
            try!(write!(f, ", ext=\"{}\"", ext));
        }
        if let Some(ref hash) = self.hash {
            try!(write!(f, ", hash=\"{}\"", hash.to_base64(base64_config)));
        }
        if let Some(ref app) = self.app {
            try!(write!(f, ", app=\"{}\"", app));
        }
        if let Some(ref dlg) = self.dlg {
            try!(write!(f, ", dlg=\"{}\"", dlg));
        }
        Ok(())
    }
}

impl fmt::Display for HawkScheme {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.fmt_scheme(f)
    }
}

impl FromStr for HawkScheme {
    type Err = Error;
    fn from_str(s: &str) -> Result<HawkScheme, Error> {
        // Check that it starts with "HAWK " (Space not optional)
        if s.len() < 5 || !s[..5].eq_ignore_ascii_case("hawk ") {
            return Err(Error::UnsupportedScheme);
        }

        let mut p = &s[4..];

        // Required attributes
        let mut id: Option<&str> = None;
        let mut ts: Option<Timespec> = None;
        let mut nonce: Option<&str> = None;
        let mut mac: Option<Vec<u8>> = None;
        // Optional attributes
        let mut hash: Option<Vec<u8>> = None;
        let mut ext: Option<&str> = None;
        let mut app: Option<&str> = None;
        let mut dlg: Option<&str> = None;

        while p.len() > 0 {
            // Skip whitespace and commas used as separators
            p = p.trim_left_matches(|c| {
                return c == ',' || char::is_whitespace(c);
            });
            // Find first '=' which delimits attribute name from value
            match p.find("=") {
                Some(v) => {
                    let attr = &p[..v].trim();
                    if p.len() < v + 1 {
                        return Err(Error::SchemeParseError);
                    }
                    p = (&p[v + 1..]).trim_left();
                    let mut end: Option<usize>;
                    if p.starts_with("\"") {
                        p = &p[1..];
                        // We have poor RFC 7235 compliance here as we ought to
                        // support backslash escaped characters, but hawk doesn't allow this
                        // we won't either.
                        end = p.find("\"");
                    } else {
                        // Parse tokens for better RFC 7235 compliance, not allowed by hawk
                        // and we shall not serialize to this format
                        end = p.find(|c| {
                            return match c {
                                '!' => false,
                                '#' => false,
                                '$' => false,
                                '%' => false,
                                '&' => false,
                                '\'' => false,
                                '*' => false,
                                '+' => false,
                                '-' => false,
                                '.' => false,
                                '^' => false,
                                '_' => false,
                                '`' => false,
                                '|' => false,
                                '~' => false,
                                _ => !char::is_alphanumeric(c) && !char::is_numeric(c),
                            };
                        });
                        end = match end {
                            Some(v) => Some(v),
                            None => Some(p.len()),
                        };
                    }
                    match end {
                        Some(v) => {
                            let val = &p[..v];
                            match *attr {
                                "id" => id = Some(val),
                                "ts" => {
                                    match i64::from_str(val) {
                                        Ok(sec) => ts = Some(Timespec::new(sec, 0)),
                                        Err(_) => return Err(Error::InvalidTimestamp),
                                    };
                                }
                                "mac" => {
                                    match val.from_base64() {
                                        Ok(v) => mac = Some(v),
                                        Err(_) => return Err(Error::Base64DecodeError),
                                    }
                                }
                                "nonce" => nonce = Some(val),
                                "ext" => ext = Some(val),
                                "hash" => {
                                    match val.from_base64() {
                                        Ok(v) => hash = Some(v),
                                        Err(_) => return Err(Error::Base64DecodeError),
                                    }
                                }
                                "app" => app = Some(val),
                                "dlg" => dlg = Some(val),
                                _ => return Err(Error::UnknownAttribute),
                            };
                            // Break if we are at end of string, otherwise skip separator
                            if p.len() < v + 1 {
                                break;
                            }
                            p = &p[v + 1..].trim_left();
                        }
                        None => return Err(Error::SchemeParseError),
                    }
                }
                None => return Err(Error::SchemeParseError),
            };
        }

        return match (id, ts, nonce, mac) {
            (Some(id), Some(ts), Some(nonce), Some(mac)) => {
                Ok(HawkScheme {
                    id: id.to_string(),
                    ts: ts,
                    nonce: nonce.to_string(),
                    mac: mac,
                    ext: match ext {
                        Some(ext) => Some(ext.to_string()),
                        None => None,
                    },
                    hash: hash,
                    app: match app {
                        Some(app) => Some(app.to_string()),
                        None => None,
                    },
                    dlg: match dlg {
                        Some(dlg) => Some(dlg.to_string()),
                        None => None,
                    },
                })
            }
            _ => Err(Error::MissingAttributes),
        };
    }
}

#[cfg(test)]
mod test {
    use super::HawkScheme;
    use hyper::header::Scheme;
    use std::str::FromStr;
    use time::Timespec;

    #[test]
    fn scheme_from_str_messy() {
        let s = HawkScheme::from_str("Hawk , id  =  \"dh37fgj492je\", ts=\"1353832234\", \
                                      nonce=\"j4h3g2\"  , , ext=\"some-app-ext-data\", \
                                      mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\"")
            .unwrap();
        assert!(s.id == "dh37fgj492je");
        assert!(s.ts == Timespec::new(1353832234, 0));
        assert!(s.nonce == "j4h3g2");
        assert!(s.mac ==
                vec![233, 30, 43, 87, 152, 132, 248, 211, 232, 202, 111, 150, 194, 55, 135, 206,
                     48, 6, 93, 75, 75, 52, 140, 102, 163, 91, 233, 50, 135, 233, 44, 1]);
        assert!(s.ext.unwrap() == "some-app-ext-data");
        assert!(s.app == None);
        assert!(s.dlg == None);
    }

    #[test]
    fn scheme_from_str2() {
        let s = HawkScheme::from_str("Hawk id=\"dh37fgj492je\", ts=\"1353832234\", \
                                      nonce=\"j4h3g2\", ext=\"some-app-ext-data\", \
                                      mac=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
                                      hash=\"6R4rV5iE+NPoym+WwjeHzjAGXUtLNIxmo1vpMofpLAE=\", \
                                      app=\"my-app\", dlg=\"my-authority\"")
            .unwrap();
        assert!(s.id == "dh37fgj492je");
        assert!(s.ts == Timespec::new(1353832234, 0));
        assert!(s.nonce == "j4h3g2");
        assert!(s.mac ==
                vec![233, 30, 43, 87, 152, 132, 248, 211, 232, 202, 111, 150, 194, 55, 135, 206,
                     48, 6, 93, 75, 75, 52, 140, 102, 163, 91, 233, 50, 135, 233, 44, 1]);
        assert!(s.ext.unwrap() == "some-app-ext-data");
        assert!(s.app == Some("my-app".to_string()));
        assert!(s.dlg == Some("my-authority".to_string()));
    }

    // TODO: more tests of from_str

    #[test]
    fn scheme_to_str_minimal() {
        let s = HawkScheme {
            id: "dh37fgj492je".to_string(),
            ts: Timespec::new(1353832234, 0),
            nonce: "j4h3g2".to_string(),
            mac: vec![8, 35, 182, 149, 42, 111, 33, 192, 19, 22, 94, 43, 118, 176, 65, 69, 86, 4,
                      156, 184, 85, 107, 249, 242, 172, 200, 66, 209, 57, 63, 38, 83],
            ext: None,
            hash: None,
            app: None,
            dlg: None,
        };
        let formatted = format!("{}", s);
        assert!(formatted ==
                "Hawk id=\"dh37fgj492je\", ts=1353832234, nonce=\"j4h3g2\", \
                 mac=\"CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=\"")
    }

    #[test]
    fn scheme_to_str_maximal() {
        let s = HawkScheme {
            id: "dh37fgj492je".to_string(),
            ts: Timespec::new(1353832234, 0),
            nonce: "j4h3g2".to_string(),
            mac: vec![8, 35, 182, 149, 42, 111, 33, 192, 19, 22, 94, 43, 118, 176, 65, 69, 86, 4,
                      156, 184, 85, 107, 249, 242, 172, 200, 66, 209, 57, 63, 38, 83],
            ext: Some("my-ext-value".to_string()),
            hash: Some(vec![1, 2, 3, 4]),
            app: Some("my-app".to_string()),
            dlg: Some("my-dlg".to_string()),
        };
        let formatted = format!("{}", s);
        assert!(formatted ==
                "Hawk id=\"dh37fgj492je\", ts=1353832234, nonce=\"j4h3g2\", \
                 mac=\"CCO2lSpvIcATFl4rdrBBRVYEnLhVa/nyrMhC0Tk/JlM=\", ext=\"my-ext-value\", \
                 hash=\"AQIDBA==\", app=\"my-app\", dlg=\"my-dlg\"")
    }

    // TODO: test invalid HawkScheme with `"` in some values
    // TODO: test round-tripping with all fields omitted, included
}
<|endoftext|>


<commit_before>//! Module to handle custom serde `Serializer`

mod var;

use self::var::{Map, Struct};
use crate::{
    errors::serialize::DeError,
    events::{BytesEnd, BytesStart, BytesText, Event},
    writer::Writer,
};
use serde::ser::{self, Impossible, Serialize};
use std::io::Write;

/// Serialize struct into a `Write`r
pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<(), DeError> {
    let mut serializer = Serializer::new(writer);
    value.serialize(&mut serializer)
}

/// Serialize struct into a `String`
pub fn to_string<S: Serialize>(value: &S) -> Result<String, DeError> {
    let mut writer = Vec::new();
    to_writer(&mut writer, value)?;
    let s = String::from_utf8(writer).map_err(|e| crate::errors::Error::Utf8(e.utf8_error()))?;
    Ok(s)
}

/// A Serializer
pub struct Serializer<W: Write> {
    writer: Writer<W>,
}

impl<W: Write> Serializer<W> {
    /// Creates a new `Serializer`
    pub fn new(writer: W) -> Self {
        Serializer {
            writer: Writer::new(writer),
        }
    }

    fn write_primitive<P: std::fmt::Display>(
        &mut self,
        value: P,
        escaped: bool,
    ) -> Result<(), DeError> {
        let value = value.to_string().into_bytes();
        let event = if escaped {
            BytesText::from_escaped(value)
        } else {
            BytesText::from_plain(&value)
        };
        self.writer.write_event(Event::Text(event))?;
        Ok(())
    }
}

impl<'w, W: Write> ser::Serializer for &'w mut Serializer<W> {
    type Ok = ();
    type Error = DeError;

    type SerializeSeq = Impossible<Self::Ok, DeError>;
    type SerializeTuple = Impossible<Self::Ok, DeError>;
    type SerializeTupleStruct = Impossible<Self::Ok, DeError>;
    type SerializeTupleVariant = Impossible<Self::Ok, DeError>;
    type SerializeMap = Map<'w, W>;
    type SerializeStruct = Struct<'w, W>;
    type SerializeStructVariant = Impossible<Self::Ok, DeError>;

    fn serialize_bool(self, v: bool) -> Result<Self::Ok, DeError> {
        self.write_primitive(if v { "true" } else { "false" }, true)
    }

    fn serialize_i8(self, v: i8) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i16(self, v: i16) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i32(self, v: i32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i64(self, v: i64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u16(self, v: u16) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u32(self, v: u32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u64(self, v: u64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_f32(self, v: f32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_f64(self, v: f64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_char(self, v: char) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, false)
    }

    fn serialize_str(self, value: &str) -> Result<Self::Ok, DeError> {
        self.write_primitive(value, false)
    }

    fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, DeError> {
        // TODO: I imagine you'd want to use base64 here.
        // Not sure how to roundtrip effectively though...
        Err(DeError::Unsupported("serialize_bytes"))
    }

    fn serialize_none(self) -> Result<Self::Ok, DeError> {
        Ok(())
    }

    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, DeError> {
        value.serialize(self)
    }

    fn serialize_unit(self) -> Result<Self::Ok, DeError> {
        self.serialize_none()
    }

    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, DeError> {
        self.writer
            .write_event(Event::Empty(BytesStart::borrowed_name(name.as_bytes())))?;
        Ok(())
    }

    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
    ) -> Result<Self::Ok, DeError> {
        Err(DeError::Unsupported("serialize_unit_variant"))
    }

    fn serialize_newtype_struct<T: ?Sized + Serialize>(
        self,
        _name: &'static str,
        _value: &T,
    ) -> Result<Self::Ok, DeError> {
        Err(DeError::Unsupported("serialize_newtype_struct"))
    }

    fn serialize_newtype_variant<T: ?Sized + Serialize>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok, DeError> {
        self.writer
            .write_event(Event::Start(BytesStart::borrowed_name(variant.as_bytes())))?;
        value.serialize(&mut *self)?;
        self.writer
            .write_event(Event::End(BytesEnd::borrowed(variant.as_bytes())))?;
        Ok(())
    }

    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, DeError> {
        // TODO: Figure out how to constrain the things written to only be composites
        Err(DeError::Unsupported("serialize_seq"))
    }

    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, DeError> {
        Err(DeError::Unsupported("serialize_tuple"))
    }

    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleStruct, DeError> {
        Err(DeError::Unsupported("serialize_tuple_struct"))
    }

    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleVariant, DeError> {
        Err(DeError::Unsupported("serialize_tuple_variant"))
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, DeError> {
        Ok(Map::new(self))
    }

    fn serialize_struct(
        self,
        name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, DeError> {
        self.writer
            .write_event(Event::Start(BytesStart::borrowed_name(name.as_bytes())))?;
        Ok(Struct::new(self, name))
    }

    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, DeError> {
        Err(DeError::Unsupported("serialize_struct_variant"))
    }
}
<commit_msg>add unit tests<commit_after>//! Module to handle custom serde `Serializer`

mod var;

use self::var::{Map, Struct};
use crate::{
    errors::serialize::DeError,
    events::{BytesEnd, BytesStart, BytesText, Event},
    writer::Writer,
};
use serde::ser::{self, Impossible, Serialize};
use std::io::Write;

/// Serialize struct into a `Write`r
pub fn to_writer<W: Write, S: Serialize>(writer: W, value: &S) -> Result<(), DeError> {
    let mut serializer = Serializer::new(writer);
    value.serialize(&mut serializer)
}

/// Serialize struct into a `String`
pub fn to_string<S: Serialize>(value: &S) -> Result<String, DeError> {
    let mut writer = Vec::new();
    to_writer(&mut writer, value)?;
    let s = String::from_utf8(writer).map_err(|e| crate::errors::Error::Utf8(e.utf8_error()))?;
    Ok(s)
}

/// A Serializer
pub struct Serializer<W: Write> {
    writer: Writer<W>,
}

impl<W: Write> Serializer<W> {
    /// Creates a new `Serializer`
    pub fn new(writer: W) -> Self {
        Serializer {
            writer: Writer::new(writer),
        }
    }

    fn write_primitive<P: std::fmt::Display>(
        &mut self,
        value: P,
        escaped: bool,
    ) -> Result<(), DeError> {
        let value = value.to_string().into_bytes();
        let event = if escaped {
            BytesText::from_escaped(value)
        } else {
            BytesText::from_plain(&value)
        };
        self.writer.write_event(Event::Text(event))?;
        Ok(())
    }
}

impl<'w, W: Write> ser::Serializer for &'w mut Serializer<W> {
    type Ok = ();
    type Error = DeError;

    type SerializeSeq = Impossible<Self::Ok, DeError>;
    type SerializeTuple = Impossible<Self::Ok, DeError>;
    type SerializeTupleStruct = Impossible<Self::Ok, DeError>;
    type SerializeTupleVariant = Impossible<Self::Ok, DeError>;
    type SerializeMap = Map<'w, W>;
    type SerializeStruct = Struct<'w, W>;
    type SerializeStructVariant = Impossible<Self::Ok, DeError>;

    fn serialize_bool(self, v: bool) -> Result<Self::Ok, DeError> {
        self.write_primitive(if v { "true" } else { "false" }, true)
    }

    fn serialize_i8(self, v: i8) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i16(self, v: i16) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i32(self, v: i32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_i64(self, v: i64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u16(self, v: u16) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u32(self, v: u32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_u64(self, v: u64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_f32(self, v: f32) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_f64(self, v: f64) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, true)
    }

    fn serialize_char(self, v: char) -> Result<Self::Ok, DeError> {
        self.write_primitive(v, false)
    }

    fn serialize_str(self, value: &str) -> Result<Self::Ok, DeError> {
        self.write_primitive(value, false)
    }

    fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, DeError> {
        // TODO: I imagine you'd want to use base64 here.
        // Not sure how to roundtrip effectively though...
        Err(DeError::Unsupported("serialize_bytes"))
    }

    fn serialize_none(self) -> Result<Self::Ok, DeError> {
        Ok(())
    }

    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Self::Ok, DeError> {
        value.serialize(self)
    }

    fn serialize_unit(self) -> Result<Self::Ok, DeError> {
        self.serialize_none()
    }

    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, DeError> {
        self.writer
            .write_event(Event::Empty(BytesStart::borrowed_name(name.as_bytes())))?;
        Ok(())
    }

    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
    ) -> Result<Self::Ok, DeError> {
        Err(DeError::Unsupported("serialize_unit_variant"))
    }

    fn serialize_newtype_struct<T: ?Sized + Serialize>(
        self,
        _name: &'static str,
        _value: &T,
    ) -> Result<Self::Ok, DeError> {
        Err(DeError::Unsupported("serialize_newtype_struct"))
    }

    fn serialize_newtype_variant<T: ?Sized + Serialize>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok, DeError> {
        self.writer
            .write_event(Event::Start(BytesStart::borrowed_name(variant.as_bytes())))?;
        value.serialize(&mut *self)?;
        self.writer
            .write_event(Event::End(BytesEnd::borrowed(variant.as_bytes())))?;
        Ok(())
    }

    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, DeError> {
        // TODO: Figure out how to constrain the things written to only be composites
        Err(DeError::Unsupported("serialize_seq"))
    }

    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, DeError> {
        Err(DeError::Unsupported("serialize_tuple"))
    }

    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleStruct, DeError> {
        Err(DeError::Unsupported("serialize_tuple_struct"))
    }

    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleVariant, DeError> {
        Err(DeError::Unsupported("serialize_tuple_variant"))
    }

    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, DeError> {
        Ok(Map::new(self))
    }

    fn serialize_struct(
        self,
        name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, DeError> {
        self.writer
            .write_event(Event::Start(BytesStart::borrowed_name(name.as_bytes())))?;
        Ok(Struct::new(self, name))
    }

    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, DeError> {
        Err(DeError::Unsupported("serialize_struct_variant"))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::ser::{SerializeMap, SerializeStruct};
    use serde::{Serialize, Serializer as SerSerializer};

    #[test]
    fn test_serialize_bool() {
        let inputs = vec![(true, "true"), (false, "false")];

        for (src, should_be) in inputs {
            let mut buffer = Vec::new();

            {
                let mut ser = Serializer::new(&mut buffer);
                ser.serialize_bool(src).unwrap();
            }

            let got = String::from_utf8(buffer).unwrap();
            assert_eq!(got, should_be);
        }
    }

    #[test]
    fn test_start_serialize_struct() {
        let mut buffer = Vec::new();

        {
            let mut ser = Serializer::new(&mut buffer);
            let _ = ser.serialize_struct("foo", 0).unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        assert_eq!(got, "<foo>");
    }

    #[test]
    fn test_serialize_struct_field() {
        let mut buffer = Vec::new();

        {
            let mut ser = Serializer::new(&mut buffer);
            let mut struct_ser = Struct::new(&mut ser, "baz");
            struct_ser.serialize_field("foo", "bar").unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        assert_eq!(got, "<foo>bar</foo>");
    }

    #[test]
    fn test_serialize_struct() {
        #[derive(Serialize)]
        struct Person {
            name: String,
            age: u32,
        }

        let bob = Person {
            name: "Bob".to_string(),
            age: 42,
        };
        let should_be = "<Person><name>Bob</name><age>42</age></Person>";
        let mut buffer = Vec::new();

        {
            let mut ser = Serializer::new(&mut buffer);
            bob.serialize(&mut ser).unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        assert_eq!(got, should_be);
    }

    #[test]
    fn test_serialize_map_entries() {
        let should_be = "<name>Bob</name><age>5</age>";
        let mut buffer = Vec::new();

        {
            let mut ser = Serializer::new(&mut buffer);
            let mut map = Map::new(&mut ser);
            map.serialize_entry("name", "Bob").unwrap();
            map.serialize_entry("age", "5").unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        assert_eq!(got, should_be);
    }

    #[test]
    fn test_serialize_enum() {
        #[derive(Serialize)]
        #[allow(dead_code)]
        enum Node {
            Boolean(bool),
            Number(f64),
            String(String),
        }

        let mut buffer = Vec::new();
        let should_be = "<Boolean>true</Boolean>";

        {
            let mut ser = Serializer::new(&mut buffer);
            let node = Node::Boolean(true);
            node.serialize(&mut ser).unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        assert_eq!(got, should_be);
    }

    #[test]
    #[ignore]
    fn serialize_a_list() {
        let inputs = vec![1, 2, 3, 4];

        let mut buffer = Vec::new();

        {
            let mut ser = Serializer::new(&mut buffer);
            inputs.serialize(&mut ser).unwrap();
        }

        let got = String::from_utf8(buffer).unwrap();
        println!("{}", got);
        panic!();
    }
}
<|endoftext|>


<commit_before>use actix_web::{http::StatusCode, HttpResponse, Query};
use common;
use hyper::header::LOCATION;
use mustache::MapBuilder;

#[derive(Deserialize)]
pub struct SearchQuery {
    pub query: String,
}

pub fn search_handler(request: Query<SearchQuery>) -> HttpResponse {
    let url = &format!(
        "https://api.tfl.gov.uk/StopPoint/Search/{}?modes=bus,replacement-bus",
        &request.query
    );
    let obj = match common::json_for_url(url) {
        Ok(val) => val,
        Err(val) => {
            let mut response = HttpResponse::Ok();
            response.status(StatusCode::BAD_GATEWAY);
            return response.body(val);
        }
    };

    if obj["matches"].members().count() == 1 {
        let stop = obj["matches"].members().nth(0).unwrap();
        let mut response = HttpResponse::Ok();
        response.header(LOCATION, format!("/id/{}", stop["id"].as_str().unwrap()));
        response.status(StatusCode::PERMANENT_REDIRECT);
        return response.body("");
    }
    let data = MapBuilder::new()
        .insert_vec("stops", |vecbuilder| {
            let mut vecb = vecbuilder;
            for stop in obj["matches"].members() {
                vecb = vecb.push_map(|mapbuilder| {
                    let name = stop["name"].as_str().unwrap();
                    mapbuilder
                        .insert_str("id", stop["id"].as_str().unwrap())
                        .insert_str("name", name)
                        .insert_str("escaped_name", common::query_encode(name))
                });
            }
            vecb
        }).insert_str("query", &request.query)
        .build();
    common::render_to_response("resources/templates/search.mustache", &data)
}<commit_msg>query doesn't need to be public<commit_after>use actix_web::{http::StatusCode, HttpResponse, Query};
use common;
use hyper::header::LOCATION;
use mustache::MapBuilder;

#[derive(Deserialize)]
pub struct SearchQuery {
    query: String,
}

pub fn search_handler(request: Query<SearchQuery>) -> HttpResponse {
    let url = &format!(
        "https://api.tfl.gov.uk/StopPoint/Search/{}?modes=bus,replacement-bus",
        &request.query
    );
    let obj = match common::json_for_url(url) {
        Ok(val) => val,
        Err(val) => {
            let mut response = HttpResponse::Ok();
            response.status(StatusCode::BAD_GATEWAY);
            return response.body(val);
        }
    };

    if obj["matches"].members().count() == 1 {
        let stop = obj["matches"].members().nth(0).unwrap();
        let mut response = HttpResponse::Ok();
        response.header(LOCATION, format!("/id/{}", stop["id"].as_str().unwrap()));
        response.status(StatusCode::PERMANENT_REDIRECT);
        return response.body("");
    }
    let data = MapBuilder::new()
        .insert_vec("stops", |vecbuilder| {
            let mut vecb = vecbuilder;
            for stop in obj["matches"].members() {
                vecb = vecb.push_map(|mapbuilder| {
                    let name = stop["name"].as_str().unwrap();
                    mapbuilder
                        .insert_str("id", stop["id"].as_str().unwrap())
                        .insert_str("name", name)
                        .insert_str("escaped_name", common::query_encode(name))
                });
            }
            vecb
        }).insert_str("query", &request.query)
        .build();
    common::render_to_response("resources/templates/search.mustache", &data)
}<|endoftext|>


<commit_before>use range::Range;

use {
    MetaReader,
    ParseError,
    Rule,
};

/// Stores information about select.
pub struct Select {
    /// The rules to select from.
    pub args: Vec<Rule>,
}

impl Select {
    /// Parses select.
    pub fn parse<M>(
        &self,
        meta_reader: &mut M,
        state: &M::State,
        chars: &[char],
        offset: usize
    ) -> Result<(Range, M::State), (Range, ParseError)>
        where M: MetaReader
    {
        let mut first_error: Option<(Range, ParseError)> = None;
        for sub_rule in &self.args {
            match sub_rule.parse(meta_reader, state, chars, offset) {
                Ok((range, state)) => {
                    return Ok((Range::new(offset, range.next_offset()), state));
                }
                Err(err) => {
                    first_error = Some(err);
                }
            }
        }
        match first_error {
            None => Err((Range::new(offset, 0), ParseError::InvalidRule(
                "`Select` requires at least one sub rule"))),
            Some(err) => Err(err),
        }
    }
}
<commit_msg>Added parse unit tests for `Select`<commit_after>use range::Range;

use {
    MetaReader,
    ParseError,
    Rule,
};

/// Stores information about select.
pub struct Select {
    /// The rules to select from.
    pub args: Vec<Rule>,
}

impl Select {
    /// Parses select.
    pub fn parse<M>(
        &self,
        meta_reader: &mut M,
        state: &M::State,
        chars: &[char],
        offset: usize
    ) -> Result<(Range, M::State), (Range, ParseError)>
        where M: MetaReader
    {
        let mut first_error: Option<(Range, ParseError)> = None;
        for sub_rule in &self.args {
            match sub_rule.parse(meta_reader, state, chars, offset) {
                Ok((range, state)) => {
                    return Ok((Range::new(offset, range.next_offset()), state));
                }
                Err(err) => {
                    first_error = Some(err);
                }
            }
        }
        match first_error {
            None => Err((Range::new(offset, 0), ParseError::InvalidRule(
                "`Select` requires at least one sub rule"))),
            Some(err) => Err(err),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::*;
    use range::Range;
    use std::rc::Rc;

    #[test]
    fn invalid_rule() {
        let text = "";
        let chars: Vec<char> = text.chars().collect();
        let mut tokenizer = Tokenizer::new();
        let s = TokenizerState::new();
        let select = Select {
            args: vec![]
        };
        let res = select.parse(&mut tokenizer, &s, &chars, 0);
        let invalid_rule = match &res {
            &Err((_, ParseError::InvalidRule(_))) => true,
            _ => false
        };
        assert!(invalid_rule);
    }

    #[test]
    fn fail_first() {
        let text = "2";
        let chars: Vec<char> = text.chars().collect();
        let mut tokenizer = Tokenizer::new();
        let s = TokenizerState::new();
        let num: Rc<String> = Rc::new("num".into());
        let select = Select {
            args: vec![
                Rule::Text(Text {
                    allow_empty: true,
                    property: None
                }),
                Rule::Number(Number {
                    property: Some(num.clone())
                })
            ]
        };
        let res = select.parse(&mut tokenizer, &s, &chars, 0);
        assert_eq!(res, Ok((Range::new(0, 1), TokenizerState(1))));
        assert_eq!(tokenizer.tokens.len(), 1);
        assert_eq!(&tokenizer.tokens[0].0, &MetaData::F64(num.clone(), 2.0));
    }
}
<|endoftext|>