<commit_before>use std::io;

fn main() {
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(_) => {
            let v: Vec<&str> = input.trim().split(", ").collect();
            println!("{}", solve(&v));
        },
        Err(error) => println!("error: {}", error),
    }
}

const N: isize = 0;
const E: isize = 1;
const S: isize = 2;
const W: isize = 3;

enum Direction {
    Right,
    Left,
}

fn solve(v: &Vec<&str>) -> isize {
    let mut x: isize = 0;
    let mut y: isize = 0;
    let mut dir = 0; // start facing north

    for step in v {
        let (direction, distance) = get_travel(step);
        match direction {
            Direction::Right => {
                dir = (dir + 1) % 4; // turn right, modulus for full rotation
            },
            Direction::Left => {
                if dir == 0 {
                    dir = 3
                } else {
                    dir = dir - 1
                }
            }
        }

        match dir {
            N => {
                y = y + distance
            },
            E => {
                x = x + distance
            },
            S => {
                y = y - distance
            },
            W => {
                x = x - distance
            },
            _ => unreachable!(),
        }
        println!("{} {} {} {}", dir, distance, x, y);
    }

    x.abs() + y.abs()
}

fn get_travel(v: &str) -> (Direction, isize) {
    let mut cs = v.chars();
    let dir = cs.next().unwrap();
    let num = isize::from_str_radix(cs.as_str(), 10).unwrap();
    match dir {
        'R' => (Direction::Right, num),
        'L' => (Direction::Left, num),
        _ => panic!("{:?}", dir),
    }
}<commit_msg>Solution for day 1 now does both parts<commit_after>use std::io;
use std::collections::HashSet;

fn main() {
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(_) => {
            let v: Vec<&str> = input.trim().split(", ").collect();
            println!("Ending is {} blocks away", solve(&v));
        },
        Err(error) => println!("error: {}", error),
    }
}

const N: isize = 0;
const E: isize = 1;
const S: isize = 2;
const W: isize = 3;

enum Direction {
    Right,
    Left,
}

fn solve(v: &Vec<&str>) -> isize {
    let mut x: isize = 0;
    let mut y: isize = 0;
    let mut dir = 0; // start facing north
    let mut visited = HashSet::new();
    let mut first = false;

    for step in v {
        let (direction, mut distance) = get_travel(step);
        match direction {
            Direction::Right => {
                dir = (dir + 1) % 4; // turn right, modulus for full rotation
            },
            Direction::Left => {
                if dir == 0 {
                    dir = 3
                } else {
                    dir = dir - 1
                }
            }
        }

        while distance > 0 {
            match dir {
                N => {
                    y = y + 1
                },
                E => {
                    x = x + 1
                },
                S => {
                    y = y - 1
                },
                W => {
                    x = x - 1
                },
                _ => unreachable!(),
            }
            distance -= 1;

            if first == false && visited.contains(&(x,y)) {
                println!("First doubly visited location is {:?} which is {} blocks away",
                    (x,y), x.abs()+y.abs());
                first = true;
            }
            visited.insert((x,y));
        }
    }

    x.abs() + y.abs()
}

fn get_travel(v: &str) -> (Direction, isize) {
    let mut cs = v.chars();
    let dir = cs.next().unwrap();
    let num = isize::from_str_radix(cs.as_str(), 10).unwrap();
    match dir {
        'R' => (Direction::Right, num),
        'L' => (Direction::Left, num),
        _ => panic!("{:?}", dir),
    }
}<|endoftext|>


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

extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;

use gleam::gl;
use std::env;
use std::path::PathBuf;
use webrender_traits::{ClipId, ClipRegion, ColorF, Epoch};
use webrender_traits::{DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
use webrender_traits::{PipelineId, RenderApi, TransformStyle};

struct ScrollState {
    root_clip_id: ClipId,
    root_scroller_pos: LayoutPoint,
    nested_clip_id: ClipId,
    nested_scroller_pos: LayoutPoint,
}

impl ScrollState {
    pub fn new(root: ClipId, nested: ClipId) -> ScrollState {
        ScrollState {
            root_clip_id: root,
            root_scroller_pos: LayoutPoint::zero(),
            nested_clip_id: nested,
            nested_scroller_pos: LayoutPoint::zero(),
        }
    }

    pub fn scroll(&mut self, root: bool, delta: (i32, i32), api: &RenderApi) {
        if root {
            self.root_scroller_pos.x += delta.0 as f32;
            self.root_scroller_pos.y += delta.1 as f32;
            api.scroll_node_with_id(self.root_scroller_pos, self.root_clip_id);
        } else {
            self.nested_scroller_pos.x += delta.0 as f32;
            self.nested_scroller_pos.y += delta.1 as f32;
            api.scroll_node_with_id(self.nested_scroller_pos, self.nested_clip_id);
        }
    }
}

struct Notifier {
    window_proxy: glutin::WindowProxy,
}

impl Notifier {
    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
        Notifier {
            window_proxy: window_proxy,
        }
    }
}

impl webrender_traits::RenderNotifier for Notifier {
    fn new_frame_ready(&mut self) {
        #[cfg(not(target_os = "android"))]
        self.window_proxy.wakeup_event_loop();
    }

    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
        #[cfg(not(target_os = "android"))]
        self.window_proxy.wakeup_event_loop();
    }
}

trait HandyDandyRectBuilder {
    fn to(&self, x2: i32, y2: i32) -> LayoutRect;
}
// Allows doing `(x, y).to(x2, y2)` to build a LayoutRect
impl HandyDandyRectBuilder for (i32, i32) {
    fn to(&self, x2: i32, y2: i32) -> LayoutRect {
        LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
                        LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32))
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let res_path = if args.len() > 1 {
        Some(PathBuf::from(&args[1]))
    } else {
        None
    };

    let window = glutin::WindowBuilder::new()
                .with_title("WebRender Scrolling Sample")
                .with_gl(glutin::GlRequest::GlThenGles {
                    opengl_version: (3, 2),
                    opengles_version: (3, 0)
                })
                .build()
                .unwrap();

    unsafe {
        window.make_current().ok();
    }

    let gl = match gl::GlType::default() {
        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
    };

    println!("OpenGL version {}", gl.get_string(gl::VERSION));
    println!("Shader resource path: {:?}", res_path);

    let (width, height) = window.get_inner_size_pixels().unwrap();

    let opts = webrender::RendererOptions {
        resource_override_path: res_path,
        debug: true,
        precache_shaders: true,
        device_pixel_ratio: window.hidpi_factor(),
        .. Default::default()
    };

    let size = DeviceUintSize::new(width, height);
    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
    let api = sender.create_api();

    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
    renderer.set_render_notifier(notifier);

    let epoch = Epoch(0);
    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);

    let pipeline_id = PipelineId(0, 0);
    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id);

    let bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(width as f32, height as f32));
    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
                                  bounds,
                                  None,
                                  TransformStyle::Flat,
                                  None,
                                  webrender_traits::MixBlendMode::Normal,
                                  Vec::new());

    if true {   // scrolling and clips stuff
        // let's make a scrollbox
        let scrollbox = (0, 0).to(300, 400);
        builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
                                      LayoutRect::new(LayoutPoint::new(10.0, 10.0),
                                                      LayoutSize::zero()),
                                      None,
                                      TransformStyle::Flat,
                                      None,
                                      webrender_traits::MixBlendMode::Normal,
                                      Vec::new());
        // set the scrolling clip
        let clip_id = builder.define_clip((0, 0).to(1000, 1000),
                                          ClipRegion::simple(&scrollbox),
                                          Some(ClipId::new(42, pipeline_id)));
        builder.push_clip_id(clip_id);
        // now put some content into it.
        // start with a white background
        builder.push_rect((0, 0).to(500, 500),
                          ClipRegion::simple(&(0, 0).to(1000, 1000)),
                          ColorF::new(1.0, 1.0, 1.0, 1.0));
        // let's make a 50x50 blue square as a visual reference
        builder.push_rect((0, 0).to(50, 50),
                          ClipRegion::simple(&(0, 0).to(50, 50)),
                          ColorF::new(0.0, 0.0, 1.0, 1.0));
        // and a 50x50 green square next to it with an offset clip
        // to see what that looks like
        builder.push_rect((50, 0).to(100, 50),
                          ClipRegion::simple(&(60, 10).to(110, 60)),
                          ColorF::new(0.0, 1.0, 0.0, 1.0));

        // Below the above rectangles, set up a nested scrollbox. It's still in
        // the same stacking context, so note that the rects passed in need to
        // be relative to the stacking context.
        let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
                                                 ClipRegion::simple(&(0, 100).to(200, 300)),
                                                 Some(ClipId::new(43, pipeline_id)));
        builder.push_clip_id(nested_clip_id);
        // give it a giant gray background just to distinguish it and to easily
        // visually identify the nested scrollbox
        builder.push_rect((-1000, -1000).to(5000, 5000),
                          ClipRegion::simple(&(-1000, -1000).to(5000, 5000)),
                          ColorF::new(0.5, 0.5, 0.5, 1.0));
        // add a teal square to visualize the scrolling/clipping behaviour
        // as you scroll the nested scrollbox with WASD keys
        builder.push_rect((0, 100).to(50, 150),
                          ClipRegion::simple(&(0, 100).to(50, 150)),
                          ColorF::new(0.0, 1.0, 1.0, 1.0));
        // just for good measure add another teal square in the bottom-right
        // corner of the nested scrollframe content, which can be scrolled into
        // view by the user
        builder.push_rect((250, 350).to(300, 400),
                          ClipRegion::simple(&(250, 350).to(300, 400)),
                          ColorF::new(0.0, 1.0, 1.0, 1.0));
        builder.pop_clip_id(); // nested_clip_id

        builder.pop_clip_id(); // clip_id
        builder.pop_stacking_context();
    }

    builder.pop_stacking_context();

    api.set_display_list(
        Some(root_background_color),
        epoch,
        LayoutSize::new(width as f32, height as f32),
        builder.finalize(),
        true);
    api.set_root_pipeline(pipeline_id);
    api.generate_frame(None);

    let mut scroll_state = ScrollState::new(ClipId::new(42, pipeline_id),
                                            ClipId::new(43, pipeline_id));

    'outer: for event in window.wait_events() {
        let mut events = Vec::new();
        events.push(event);

        for event in window.poll_events() {
            events.push(event);
        }

        for event in events {
            match event {
                glutin::Event::Closed |
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
                // Use up/down/left/right for scrolling the outer scrollbox
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::Down)) => {
                    scroll_state.scroll(true, (0, 10), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::Up)) => {
                    scroll_state.scroll(true, (0, -10), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::Left)) => {
                    scroll_state.scroll(true, (-10, 0), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::Right)) => {
                    scroll_state.scroll(true, (10, 0), &api);
                }
                // Use WASD for scrolling the nested scrollbox
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::S)) => {
                    scroll_state.scroll(false, (0, 10), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::W)) => {
                    scroll_state.scroll(false, (0, -10), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::A)) => {
                    scroll_state.scroll(false, (-10, 0), &api);
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _, Some(glutin::VirtualKeyCode::D)) => {
                    scroll_state.scroll(false, (10, 0), &api);
                }
                _ => ()
            }
        }

        renderer.update();
        renderer.render(DeviceUintSize::new(width, height));
        window.swap_buffers().ok();
    }
}
<commit_msg>Auto merge of #1164 - mrobinson:scrolling-example, r=kvark<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;

use gleam::gl;
use std::env;
use std::path::PathBuf;
use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintSize, Epoch, LayoutPoint, LayoutRect};
use webrender_traits::{LayoutSize, PipelineId, ScrollEventPhase, ScrollLocation, TransformStyle};
use webrender_traits::WorldPoint;

struct Notifier {
    window_proxy: glutin::WindowProxy,
}

impl Notifier {
    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
        Notifier {
            window_proxy: window_proxy,
        }
    }
}

impl webrender_traits::RenderNotifier for Notifier {
    fn new_frame_ready(&mut self) {
        #[cfg(not(target_os = "android"))]
        self.window_proxy.wakeup_event_loop();
    }

    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
        #[cfg(not(target_os = "android"))]
        self.window_proxy.wakeup_event_loop();
    }
}

trait HandyDandyRectBuilder {
    fn to(&self, x2: i32, y2: i32) -> LayoutRect;
}
// Allows doing `(x, y).to(x2, y2)` to build a LayoutRect
impl HandyDandyRectBuilder for (i32, i32) {
    fn to(&self, x2: i32, y2: i32) -> LayoutRect {
        LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
                        LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32))
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let res_path = if args.len() > 1 {
        Some(PathBuf::from(&args[1]))
    } else {
        None
    };

    let window = glutin::WindowBuilder::new()
                .with_title("WebRender Scrolling Sample")
                .with_gl(glutin::GlRequest::GlThenGles {
                    opengl_version: (3, 2),
                    opengles_version: (3, 0)
                })
                .build()
                .unwrap();

    unsafe {
        window.make_current().ok();
    }

    let gl = match gl::GlType::default() {
        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
    };

    println!("OpenGL version {}", gl.get_string(gl::VERSION));
    println!("Shader resource path: {:?}", res_path);

    let (width, height) = window.get_inner_size_pixels().unwrap();

    let opts = webrender::RendererOptions {
        resource_override_path: res_path,
        debug: true,
        precache_shaders: true,
        device_pixel_ratio: window.hidpi_factor(),
        .. Default::default()
    };

    let size = DeviceUintSize::new(width, height);
    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
    let api = sender.create_api();

    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
    renderer.set_render_notifier(notifier);

    let epoch = Epoch(0);
    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);

    let pipeline_id = PipelineId(0, 0);
    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id);

    let bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(width as f32, height as f32));
    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
                                  bounds,
                                  None,
                                  TransformStyle::Flat,
                                  None,
                                  webrender_traits::MixBlendMode::Normal,
                                  Vec::new());

    if true {   // scrolling and clips stuff
        // let's make a scrollbox
        let scrollbox = (0, 0).to(300, 400);
        builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
                                      LayoutRect::new(LayoutPoint::new(10.0, 10.0),
                                                      LayoutSize::zero()),
                                      None,
                                      TransformStyle::Flat,
                                      None,
                                      webrender_traits::MixBlendMode::Normal,
                                      Vec::new());
        // set the scrolling clip
        let clip_id = builder.define_clip((0, 0).to(1000, 1000),
                                          ClipRegion::simple(&scrollbox),
                                          Some(ClipId::new(42, pipeline_id)));
        builder.push_clip_id(clip_id);
        // now put some content into it.
        // start with a white background
        builder.push_rect((0, 0).to(500, 500),
                          ClipRegion::simple(&(0, 0).to(1000, 1000)),
                          ColorF::new(1.0, 1.0, 1.0, 1.0));
        // let's make a 50x50 blue square as a visual reference
        builder.push_rect((0, 0).to(50, 50),
                          ClipRegion::simple(&(0, 0).to(50, 50)),
                          ColorF::new(0.0, 0.0, 1.0, 1.0));
        // and a 50x50 green square next to it with an offset clip
        // to see what that looks like
        builder.push_rect((50, 0).to(100, 50),
                          ClipRegion::simple(&(60, 10).to(110, 60)),
                          ColorF::new(0.0, 1.0, 0.0, 1.0));

        // Below the above rectangles, set up a nested scrollbox. It's still in
        // the same stacking context, so note that the rects passed in need to
        // be relative to the stacking context.
        let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
                                                 ClipRegion::simple(&(0, 100).to(200, 300)),
                                                 Some(ClipId::new(43, pipeline_id)));
        builder.push_clip_id(nested_clip_id);
        // give it a giant gray background just to distinguish it and to easily
        // visually identify the nested scrollbox
        builder.push_rect((-1000, -1000).to(5000, 5000),
                          ClipRegion::simple(&(-1000, -1000).to(5000, 5000)),
                          ColorF::new(0.5, 0.5, 0.5, 1.0));
        // add a teal square to visualize the scrolling/clipping behaviour
        // as you scroll the nested scrollbox with WASD keys
        builder.push_rect((0, 100).to(50, 150),
                          ClipRegion::simple(&(0, 100).to(50, 150)),
                          ColorF::new(0.0, 1.0, 1.0, 1.0));
        // just for good measure add another teal square in the bottom-right
        // corner of the nested scrollframe content, which can be scrolled into
        // view by the user
        builder.push_rect((250, 350).to(300, 400),
                          ClipRegion::simple(&(250, 350).to(300, 400)),
                          ColorF::new(0.0, 1.0, 1.0, 1.0));
        builder.pop_clip_id(); // nested_clip_id

        builder.pop_clip_id(); // clip_id
        builder.pop_stacking_context();
    }

    builder.pop_stacking_context();

    api.set_display_list(
        Some(root_background_color),
        epoch,
        LayoutSize::new(width as f32, height as f32),
        builder.finalize(),
        true);
    api.set_root_pipeline(pipeline_id);
    api.generate_frame(None);

    let mut cursor_position = WorldPoint::zero();

    'outer: for event in window.wait_events() {
        let mut events = Vec::new();
        events.push(event);

        for event in window.poll_events() {
            events.push(event);
        }

        for event in events {
            match event {
                glutin::Event::Closed |
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
                    let offset = match key {
                         glutin::VirtualKeyCode::Down => (0.0, -10.0),
                         glutin::VirtualKeyCode::Up => (0.0, 10.0),
                         glutin::VirtualKeyCode::Right => (-10.0, 0.0),
                         glutin::VirtualKeyCode::Left => (10.0, 0.0),
                         _ => continue,
                    };

                    api.scroll(ScrollLocation::Delta(LayoutPoint::new(offset.0, offset.1)),
                               cursor_position,
                               ScrollEventPhase::Start);
                }
                glutin::Event::MouseMoved(x, y) => {
                    cursor_position = WorldPoint::new(x as f32, y as f32);
                }
                glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
                    if let Some((x, y)) = event_cursor_position {
                        cursor_position = WorldPoint::new(x as f32, y as f32);
                    }

                    const LINE_HEIGHT: f32 = 38.0;
                    let (dx, dy) = match delta {
                        glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
                        glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
                    };

                    api.scroll(ScrollLocation::Delta(LayoutPoint::new(dx, dy)),
                               cursor_position,
                               ScrollEventPhase::Start);
                }
                _ => ()
            }
        }

        renderer.update();
        renderer.render(DeviceUintSize::new(width, height));
        window.swap_buffers().ok();
    }
}
<|endoftext|>


<commit_before>// Copyright © 2018, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use lazy_static::lazy_static;
use mimir::rubber::{IndexSettings, Rubber};
use mimirsbrunn::addr_reader::import_addresses;
use mimirsbrunn::admin_geofinder::AdminGeoFinder;
use mimirsbrunn::{labels, utils};
use serde::{Deserialize, Serialize};
use slog_scope::{info, warn};
use std::ops::Deref;
use std::path::PathBuf;
use structopt::StructOpt;

lazy_static! {
    static ref DEFAULT_NB_THREADS: String = num_cpus::get().to_string();
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct OpenAddresse {
    pub id: String,
    pub street: String,
    pub postcode: String,
    pub district: String,
    pub region: String,
    pub city: String,
    pub number: String,
    pub unit: String,
    pub lat: f64,
    pub lon: f64,
}

impl OpenAddresse {
    pub fn into_addr(
        self,
        admins_geofinder: &AdminGeoFinder,
        use_old_index_format: bool,
    ) -> Result<mimir::Addr, mimirsbrunn::Error> {
        let street_id = format!("street:{}", self.id); // TODO check if thats ok
        let admins = admins_geofinder.get(&geo::Coordinate {
            x: self.lon,
            y: self.lat,
        });
        let country_codes = utils::find_country_codes(admins.iter().map(|a| a.deref()));

        let weight = admins.iter().find(|a| a.is_city()).map_or(0., |a| a.weight);
        // Note: for openaddress, we don't trust the admin hierarchy much (compared to bano)
        // so we use for the label the admins that we find in the DB
        let street_label = labels::format_street_label(
            &self.street,
            admins.iter().map(|a| a.deref()),
            &country_codes,
        );
        let (addr_name, addr_label) = labels::format_addr_name_and_label(
            &self.number,
            &self.street,
            admins.iter().map(|a| a.deref()),
            &country_codes,
        );

        let coord = mimir::Coord::new(self.lon, self.lat);
        let street = mimir::Street {
            id: street_id,
            name: self.street,
            label: street_label,
            administrative_regions: admins,
            weight: weight,
            zip_codes: vec![self.postcode.clone()],
            coord: coord.clone(),
            approx_coord: None,
            distance: None,
            country_codes: country_codes.clone(),
            context: None,
        };

        Ok(mimir::Addr {
            id: format!(
                "addr:{};{}{}",
                self.lon,
                self.lat,
                if use_old_index_format {
                    String::new()
                } else {
                    format!(
                        ":{}",
                        self.number
                            .replace(" ", "")
                            .replace("\t", "")
                            .replace("\r", "")
                            .replace("\n", "")
                            .replace("/", "-")
                            .replace(".", "-")
                            .replace(":", "-")
                            .replace(";", "-")
                    )
                }
            ),
            name: addr_name,
            label: addr_label,
            house_number: self.number,
            street: street,
            coord: coord.clone(),
            approx_coord: Some(coord.into()),
            weight: weight,
            zip_codes: vec![self.postcode],
            distance: None,
            country_codes,
            context: None,
        })
    }
}

fn index_oa<I>(
    cnx_string: &str,
    dataset: &str,
    index_settings: IndexSettings,
    files: I,
    nb_threads: usize,
    use_old_index_format: bool,
) -> Result<(), mimirsbrunn::Error>
where
    I: Iterator<Item = std::path::PathBuf>,
{
    let mut rubber = Rubber::new(cnx_string);

    let admins = rubber.get_all_admins().unwrap_or_else(|err| {
        warn!(
            "Administratives regions not found in es db for dataset {}. (error: {})",
            dataset, err
        );
        vec![]
    });
    let admins_geofinder = admins.into_iter().collect();

    import_addresses(
        &mut rubber,
        true,
        nb_threads,
        index_settings,
        dataset,
        files,
        move |a: OpenAddresse| a.into_addr(&admins_geofinder, use_old_index_format),
    )
}

#[derive(StructOpt, Debug)]
struct Args {
    /// openaddresses files. Can be either a directory or a file.
    #[structopt(short = "i", long = "input", parse(from_os_str))]
    input: PathBuf,
    /// Elasticsearch parameters.
    #[structopt(
        short = "c",
        long = "connection-string",
        default_value = "http://localhost:9200/munin"
    )]
    connection_string: String,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset", default_value = "fr")]
    dataset: String,
    /// Deprecated option.
    #[structopt(short = "C", long = "city-level")]
    city_level: Option<String>,
    /// Number of threads to use
    #[structopt(
        short = "t",
        long = "nb-threads",
        default_value = &DEFAULT_NB_THREADS
    )]
    nb_threads: usize,
    /// Number of shards for the es index
    #[structopt(short = "s", long = "nb-shards", default_value = "5")]
    nb_shards: usize,
    /// Number of replicas for the es index
    #[structopt(short = "r", long = "nb-replicas", default_value = "1")]
    nb_replicas: usize,
    /// If set to true, the number inside the address won't be used for the index generation,
    /// therefore, different addresses with the same position will disappear.
    #[structopt(long = "use-old-index-format")]
    use_old_index_format: bool,
}

fn run(args: Args) -> Result<(), failure::Error> {
    info!("importing open addresses into Mimir");

    if args.city_level.is_some() {
        warn!("city-level option is deprecated, it now has no effect.");
    }

    let index_settings = IndexSettings {
        nb_shards: args.nb_shards,
        nb_replicas: args.nb_replicas,
    };
    if args.input.is_dir() {
        let paths = walkdir::WalkDir::new(&args.input);
        let path_iter = paths
            .into_iter()
            .map(|p| p.unwrap().into_path())
            .filter(|p| {
                let f = p
                    .extension()
                    .and_then(|e| e.to_str())
                    .map(|e| e == "csv")
                    .unwrap_or(false);
                if !f {
                    info!("skipping file {} as it is not a csv", p.display());
                }
                f
            });
        index_oa(
            &args.connection_string,
            &args.dataset,
            index_settings,
            path_iter,
            args.nb_threads,
            args.use_old_index_format,
        )
    } else {
        index_oa(
            &args.connection_string,
            &args.dataset,
            index_settings,
            std::iter::once(args.input),
            args.nb_threads,
            args.use_old_index_format,
        )
    }
}

fn main() {
    mimirsbrunn::utils::launch_run(run);
}
<commit_msg>Fix struct name typo<commit_after>// Copyright © 2018, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use lazy_static::lazy_static;
use mimir::rubber::{IndexSettings, Rubber};
use mimirsbrunn::addr_reader::import_addresses;
use mimirsbrunn::admin_geofinder::AdminGeoFinder;
use mimirsbrunn::{labels, utils};
use serde::{Deserialize, Serialize};
use slog_scope::{info, warn};
use std::ops::Deref;
use std::path::PathBuf;
use structopt::StructOpt;

lazy_static! {
    static ref DEFAULT_NB_THREADS: String = num_cpus::get().to_string();
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct OpenAddress {
    pub id: String,
    pub street: String,
    pub postcode: String,
    pub district: String,
    pub region: String,
    pub city: String,
    pub number: String,
    pub unit: String,
    pub lat: f64,
    pub lon: f64,
}

impl OpenAddress {
    pub fn into_addr(
        self,
        admins_geofinder: &AdminGeoFinder,
        use_old_index_format: bool,
    ) -> Result<mimir::Addr, mimirsbrunn::Error> {
        let street_id = format!("street:{}", self.id); // TODO check if thats ok
        let admins = admins_geofinder.get(&geo::Coordinate {
            x: self.lon,
            y: self.lat,
        });
        let country_codes = utils::find_country_codes(admins.iter().map(|a| a.deref()));

        let weight = admins.iter().find(|a| a.is_city()).map_or(0., |a| a.weight);
        // Note: for openaddress, we don't trust the admin hierarchy much (compared to bano)
        // so we use for the label the admins that we find in the DB
        let street_label = labels::format_street_label(
            &self.street,
            admins.iter().map(|a| a.deref()),
            &country_codes,
        );
        let (addr_name, addr_label) = labels::format_addr_name_and_label(
            &self.number,
            &self.street,
            admins.iter().map(|a| a.deref()),
            &country_codes,
        );

        let coord = mimir::Coord::new(self.lon, self.lat);
        let street = mimir::Street {
            id: street_id,
            name: self.street,
            label: street_label,
            administrative_regions: admins,
            weight: weight,
            zip_codes: vec![self.postcode.clone()],
            coord: coord.clone(),
            approx_coord: None,
            distance: None,
            country_codes: country_codes.clone(),
            context: None,
        };

        Ok(mimir::Addr {
            id: format!(
                "addr:{};{}{}",
                self.lon,
                self.lat,
                if use_old_index_format {
                    String::new()
                } else {
                    format!(
                        ":{}",
                        self.number
                            .replace(" ", "")
                            .replace("\t", "")
                            .replace("\r", "")
                            .replace("\n", "")
                            .replace("/", "-")
                            .replace(".", "-")
                            .replace(":", "-")
                            .replace(";", "-")
                    )
                }
            ),
            name: addr_name,
            label: addr_label,
            house_number: self.number,
            street: street,
            coord: coord.clone(),
            approx_coord: Some(coord.into()),
            weight: weight,
            zip_codes: vec![self.postcode],
            distance: None,
            country_codes,
            context: None,
        })
    }
}

fn index_oa<I>(
    cnx_string: &str,
    dataset: &str,
    index_settings: IndexSettings,
    files: I,
    nb_threads: usize,
    use_old_index_format: bool,
) -> Result<(), mimirsbrunn::Error>
where
    I: Iterator<Item = std::path::PathBuf>,
{
    let mut rubber = Rubber::new(cnx_string);

    let admins = rubber.get_all_admins().unwrap_or_else(|err| {
        warn!(
            "Administratives regions not found in es db for dataset {}. (error: {})",
            dataset, err
        );
        vec![]
    });
    let admins_geofinder = admins.into_iter().collect();

    import_addresses(
        &mut rubber,
        true,
        nb_threads,
        index_settings,
        dataset,
        files,
        move |a: OpenAddress| a.into_addr(&admins_geofinder, use_old_index_format),
    )
}

#[derive(StructOpt, Debug)]
struct Args {
    /// openaddresses files. Can be either a directory or a file.
    #[structopt(short = "i", long = "input", parse(from_os_str))]
    input: PathBuf,
    /// Elasticsearch parameters.
    #[structopt(
        short = "c",
        long = "connection-string",
        default_value = "http://localhost:9200/munin"
    )]
    connection_string: String,
    /// Name of the dataset.
    #[structopt(short = "d", long = "dataset", default_value = "fr")]
    dataset: String,
    /// Deprecated option.
    #[structopt(short = "C", long = "city-level")]
    city_level: Option<String>,
    /// Number of threads to use
    #[structopt(
        short = "t",
        long = "nb-threads",
        default_value = &DEFAULT_NB_THREADS
    )]
    nb_threads: usize,
    /// Number of shards for the es index
    #[structopt(short = "s", long = "nb-shards", default_value = "5")]
    nb_shards: usize,
    /// Number of replicas for the es index
    #[structopt(short = "r", long = "nb-replicas", default_value = "1")]
    nb_replicas: usize,
    /// If set to true, the number inside the address won't be used for the index generation,
    /// therefore, different addresses with the same position will disappear.
    #[structopt(long = "use-old-index-format")]
    use_old_index_format: bool,
}

fn run(args: Args) -> Result<(), failure::Error> {
    info!("importing open addresses into Mimir");

    if args.city_level.is_some() {
        warn!("city-level option is deprecated, it now has no effect.");
    }

    let index_settings = IndexSettings {
        nb_shards: args.nb_shards,
        nb_replicas: args.nb_replicas,
    };
    if args.input.is_dir() {
        let paths = walkdir::WalkDir::new(&args.input);
        let path_iter = paths
            .into_iter()
            .map(|p| p.unwrap().into_path())
            .filter(|p| {
                let f = p
                    .extension()
                    .and_then(|e| e.to_str())
                    .map(|e| e == "csv")
                    .unwrap_or(false);
                if !f {
                    info!("skipping file {} as it is not a csv", p.display());
                }
                f
            });
        index_oa(
            &args.connection_string,
            &args.dataset,
            index_settings,
            path_iter,
            args.nb_threads,
            args.use_old_index_format,
        )
    } else {
        index_oa(
            &args.connection_string,
            &args.dataset,
            index_settings,
            std::iter::once(args.input),
            args.nb_threads,
            args.use_old_index_format,
        )
    }
}

fn main() {
    mimirsbrunn::utils::launch_run(run);
}
<|endoftext|>


<commit_before>use nom::{be_u8, be_u16};

use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::convert::AsMut;
use std::convert::From;

use network::protocol::proxy_protocol::header::*;

const PROTOCOL_SIGNATURE_V2: [u8; 12] = [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A];

named!(parse_command<Command>,
  switch!(be_u8,
    0x20 => value!(Command::Local) |
    0x21 => value!(Command::Proxy)
  )
);

named!(pub parse_v2_header<HeaderV2>,
  do_parse!(
    signature: tag!(&PROTOCOL_SIGNATURE_V2) >>
    command: parse_command >>
    family: be_u8 >>
    len: be_u16 >>
    addr: apply!(parse_addr_v2, family) >>
    (
      HeaderV2 {
        command,
        family,
        addr
      }
    )
  )
);

named_args!(address_family(family: u8)<u8>, value!((family >> 4) & 0x0f));

named!(unspec<ProxyAddr>, value!(ProxyAddr::AfUnspec));

named_args!(parse_addr_v2(family: u8)<ProxyAddr>,
  // family: 4 bits for address family and 4 bits for transport protocol
  switch!(apply!(address_family, family),
    0x00 => call!(unspec) |
    0x01 => call!(parse_ipv4_on_v2) |
    0x02 => call!(parse_ipv6_on_v2)
  )
);

named!(take_4_bytes, take!(4));

named!(parse_ipv4_on_v2<ProxyAddr>,
  do_parse!(
    src_ip: take_4_bytes >>
    dest_ip: take_4_bytes >>
    src_port: be_u16 >>
    dest_port: be_u16 >>
    (
      ProxyAddr::Ipv4Addr {
        src_addr: SocketAddrV4::new(Ipv4Addr::new(src_ip[0], src_ip[1], src_ip[2], src_ip[3]), src_port),
        dst_addr: SocketAddrV4::new(Ipv4Addr::new(dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3]), dest_port)
      }
    )
  )
);

named!(take_16_bytes, take!(16));

named!(parse_ipv6_on_v2<ProxyAddr>,
  do_parse!(
    src_ip: take_16_bytes >>
    dest_ip: take_16_bytes >>
    src_port: be_u16 >>
    dest_port: be_u16 >>
    (
      ProxyAddr::Ipv6Addr {
        src_addr: SocketAddrV6::new(slice_to_ipv6(&src_ip[..]), src_port, 0 ,0),
        dst_addr: SocketAddrV6::new(slice_to_ipv6(&dest_ip[..]), dest_port, 0 ,0),
      }
    )
  )
);

// assumes the slice has 16 bytes
pub fn slice_to_ipv6(sl: &[u8]) -> Ipv6Addr {
  let mut arr: [u8; 16] = [0; 16];
  arr.clone_from_slice(sl);
  Ipv6Addr::from(arr)
}

#[cfg(test)]
mod test {

  use super::*;
  use nom::ErrorKind;
  use nom::IResult;
  use nom::IResult::*;
  use std::net::{IpAddr, SocketAddr};
  use nom::Needed::Size;

  #[test]
  fn test_parse_proxy_protocol_v2_local_ipv4_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
    let expected = HeaderV2::new(Command::Local, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn test_parse_proxy_protocol_v2_proxy_ipv4_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x21,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
    let expected = HeaderV2::new(Command::Proxy, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_parse_proxy_protocol_v2_ipv6_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,                         // MAGIC header
      0x20,                                                                                // Version 2 and command LOCAL
      0x21,                                                                                // family AF_UNIX with IPv6
      0x00, 0x24,                                                                                // address sizes = 36
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // source address
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // destination address
      0x1F, 0x90,                                                                                // source port
      0x10, 0x68,                                                                                // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2)), 4200);
    let expected = HeaderV2::new(Command::Local, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_parse_proxy_protocol_v2_afunspec_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x00,                                                                   // family AF_UNSPEC and transport protocol unknow
      0x00, 0x00,                                                             // address sizes = 0
    ];

    let expected = HeaderV2 {
                    command: Command::Local,
                    family: 0,
                    addr: ProxyAddr::AfUnspec,
                  };

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_with_unknown_version() {
    let unknow_version = 0x30;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      unknow_version,                                                         // invalid version
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_with_unknown_command() {
    let unknow_command = 0x23;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      unknow_command,                                                         // Version 2 and invalid command
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_with_unknown_family() {
    let unknow_family = 0x30;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      unknow_family,                                                          // family
      0x00, 0x00,                                                             // address sizes = 0
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_request_without_magic_header() {

    let input = &[
      0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, // INCORRECT MAGIC header
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_ipv4_addr_header_with_missing_data() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
    ];

    assert_eq!(Incomplete(Size(16)), parse_v2_header(input));
  }
}
<commit_msg>use the length field when parsing the addresses<commit_after>use nom::{be_u8, be_u16};

use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::convert::AsMut;
use std::convert::From;

use network::protocol::proxy_protocol::header::*;

const PROTOCOL_SIGNATURE_V2: [u8; 12] = [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A];

named!(parse_command<Command>,
  switch!(be_u8,
    0x20 => value!(Command::Local) |
    0x21 => value!(Command::Proxy)
  )
);

named!(pub parse_v2_header<HeaderV2>,
  do_parse!(
    signature: tag!(&PROTOCOL_SIGNATURE_V2) >>
    command: parse_command >>
    family: be_u8 >>
    len: be_u16 >>
    addr: flat_map!(take!(len), apply!(parse_addr_v2, family)) >>
    (
      HeaderV2 {
        command,
        family,
        addr
      }
    )
  )
);

named_args!(address_family(family: u8)<u8>, value!((family >> 4) & 0x0f));

named!(unspec<ProxyAddr>, value!(ProxyAddr::AfUnspec));

named_args!(parse_addr_v2(family: u8)<ProxyAddr>,
  // family: 4 bits for address family and 4 bits for transport protocol
  switch!(apply!(address_family, family),
    0x00 => call!(unspec) |
    0x01 => call!(parse_ipv4_on_v2) |
    0x02 => call!(parse_ipv6_on_v2)
  )
);

named!(take_4_bytes, take!(4));

named!(parse_ipv4_on_v2<ProxyAddr>,
  do_parse!(
    src_ip: take_4_bytes >>
    dest_ip: take_4_bytes >>
    src_port: be_u16 >>
    dest_port: be_u16 >>
    (
      ProxyAddr::Ipv4Addr {
        src_addr: SocketAddrV4::new(Ipv4Addr::new(src_ip[0], src_ip[1], src_ip[2], src_ip[3]), src_port),
        dst_addr: SocketAddrV4::new(Ipv4Addr::new(dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3]), dest_port)
      }
    )
  )
);

named!(take_16_bytes, take!(16));

named!(parse_ipv6_on_v2<ProxyAddr>,
  do_parse!(
    src_ip: take_16_bytes >>
    dest_ip: take_16_bytes >>
    src_port: be_u16 >>
    dest_port: be_u16 >>
    (
      ProxyAddr::Ipv6Addr {
        src_addr: SocketAddrV6::new(slice_to_ipv6(&src_ip[..]), src_port, 0 ,0),
        dst_addr: SocketAddrV6::new(slice_to_ipv6(&dest_ip[..]), dest_port, 0 ,0),
      }
    )
  )
);

// assumes the slice has 16 bytes
pub fn slice_to_ipv6(sl: &[u8]) -> Ipv6Addr {
  let mut arr: [u8; 16] = [0; 16];
  arr.clone_from_slice(sl);
  Ipv6Addr::from(arr)
}

#[cfg(test)]
mod test {

  use super::*;
  use nom::ErrorKind;
  use nom::IResult;
  use nom::IResult::*;
  use std::net::{IpAddr, SocketAddr};
  use nom::Needed::Size;

  #[test]
  fn test_parse_proxy_protocol_v2_local_ipv4_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
    let expected = HeaderV2::new(Command::Local, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn test_parse_proxy_protocol_v2_proxy_ipv4_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x21,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
      0x00, 0x0C,                                                             // address sizes = 12
      0x7D, 0x19, 0x0A, 0x01,                                                 // source address
      0x0A, 0x04, 0x05, 0x08,                                                 // destination address
      0x1F, 0x90,                                                             // source port
      0x10, 0x68,                                                             // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
    let expected = HeaderV2::new(Command::Proxy, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_parse_proxy_protocol_v2_ipv6_addr_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,                         // MAGIC header
      0x20,                                                                                // Version 2 and command LOCAL
      0x21,                                                                                // family AF_UNIX with IPv6
      0x00, 0x24,                                                                                // address sizes = 36
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // source address
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // destination address
      0x1F, 0x90,                                                                                // source port
      0x10, 0x68,                                                                                // destination port
    ];

    let src_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
    let dst_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2)), 4200);
    let expected = HeaderV2::new(Command::Local, src_addr, dst_addr);

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_parse_proxy_protocol_v2_afunspec_header() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x00,                                                                   // family AF_UNSPEC and transport protocol unknow
      0x00, 0x00,                                                             // address sizes = 0
    ];

    let expected = HeaderV2 {
                    command: Command::Local,
                    family: 0,
                    addr: ProxyAddr::AfUnspec,
                  };

    assert_eq!(Done(&[][..], expected), parse_v2_header(input));
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_with_unknown_version() {
    let unknow_version = 0x30;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      unknow_version,                                                         // invalid version
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_with_unknown_command() {
    let unknow_command = 0x23;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      unknow_command,                                                         // Version 2 and invalid command
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_with_unknown_family() {
    let unknow_family = 0x30;

    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      unknow_family,                                                          // family
      0x00, 0x00,                                                             // address sizes = 0
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_request_without_magic_header() {

    let input = &[
      0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, // INCORRECT MAGIC header
    ];

    assert!(parse_v2_header(input).is_err());
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_ipv4_addr_header_with_missing_data() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A, // MAGIC header
      0x20,                                                                   // Version 2 and command LOCAL
      0x11,                                                                   // family AF_UNIX with IPv4
    ];

    assert_eq!(Incomplete(Size(16)), parse_v2_header(input));
  }

  #[test]
  fn it_should_not_parse_proxy_protocol_v2_with_invalid_length() {
    let input = &[
      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,                         // MAGIC header
      0x20,                                                                                // Version 2 and command LOCAL
      0x21,                                                                                // family AF_UNIX with IPv6
      0x00, 0x10,                                                                                // address sizes = 36
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // source address
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // destination address
      0x1F, 0x90,                                                                                // source port
      0x10, 0x68,                                                                                // destination port
    ];

    assert_eq!(Incomplete(Size(48)), parse_v2_header(input));
  }
}
<|endoftext|>


<commit_before>use core::num::NonZeroUsize;
use std::io;

#[cfg(windows)]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use core::ffi::c_void;
    use std::os::raw::c_ulong;

    #[link(name = "kernel32")]
    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    #[repr(C)]
    #[allow(non_snake_case)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: c_ulong,
        lpMinimumApplicationAddress: *mut c_void,
        lpMaximumApplicationAddress: *mut c_void,
        dwActiveProcessorMask: usize,
        dwNumberOfProcessors: c_ulong,
        dwProcessorType: c_ulong,
        dwAllocationGranularity: c_ulong,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    let res = unsafe {
        let mut sysinfo: SYSTEM_INFO = core::mem::zeroed();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    };
    match res {
        0 => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        )),
        cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
    }
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use core::ptr;

    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = core::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }

    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        let res = unsafe {
            libc::sysctl(
                mib.as_mut_ptr(),
                2,
                &mut cpus as *mut _ as *mut _,
                &mut cpus_size as *mut _ as *mut _,
                ptr::null_mut(),
                0,
            )
        };

        if res == -1 {
            return Err(io::Error::last_os_error());
        } else if cpus == 0 {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "The number of hardware threads is not known for the target platform",
            ));
        }
    }
    Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
}

#[cfg(any(
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    target_os = "ios",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use core::mem;

    #[cfg(any(target_os = "android", target_os = "linux"))]
    {
        let mut set: libc::cpu_set_t = unsafe { mem::zeroed() };
        if unsafe { libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
            let count = unsafe { libc::CPU_COUNT(&set) };
            return Ok(unsafe { NonZeroUsize::new_unchecked(count as usize) });
        }
    }
    match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
        -1 => Err(io::Error::last_os_error()),
        0 => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        )),
        cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
    }
}

#[cfg(target_os = "openbsd")]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use crate::ptr;

    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = crate::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    let res = unsafe {
        libc::sysctl(
            mib.as_mut_ptr(),
            2,
            &mut cpus as *mut _ as *mut _,
            &mut cpus_size as *mut _ as *mut _,
            ptr::null_mut(),
            0,
        )
    };

    if res == -1 {
        Err(io::Error::last_os_error())
    } else if cpus == 0 {
        Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        ))
    } else {
        Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
    }
}

#[cfg(target_os = "haiku")]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    unsafe {
        let mut sinfo: libc::system_info = crate::mem::zeroed();
        let res = libc::get_system_info(&mut sinfo);

        if res != libc::B_OK {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "The number of hardware threads is not known for the target platform",
            ));
        }

        Ok(NonZeroUsize::new_unchecked(sinfo.cpu_count as usize))
    }
}

#[cfg(not(any(
    windows,
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    target_os = "ios",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
    target_os = "openbsd",
    target_os = "haiku",
)))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    Err(io::Error::new(
        io::ErrorKind::Unsupported,
        "Getting the number of hardware threads is not supported on the target platform",
    ))
}
<commit_msg>Remove unused import<commit_after>use core::num::NonZeroUsize;
use std::io;

#[cfg(windows)]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use core::ffi::c_void;
    use std::os::raw::c_ulong;

    #[link(name = "kernel32")]
    extern "system" {
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
    }

    #[repr(C)]
    #[allow(non_snake_case)]
    struct SYSTEM_INFO {
        wProcessorArchitecture: u16,
        wReserved: u16,
        dwPageSize: c_ulong,
        lpMinimumApplicationAddress: *mut c_void,
        lpMaximumApplicationAddress: *mut c_void,
        dwActiveProcessorMask: usize,
        dwNumberOfProcessors: c_ulong,
        dwProcessorType: c_ulong,
        dwAllocationGranularity: c_ulong,
        wProcessorLevel: u16,
        wProcessorRevision: u16,
    }

    let res = unsafe {
        let mut sysinfo: SYSTEM_INFO = core::mem::zeroed();
        GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    };
    match res {
        0 => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        )),
        cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
    }
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use core::ptr;

    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = core::mem::size_of_val(&cpus);

    unsafe {
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
    }

    if cpus < 1 {
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
        let res = unsafe {
            libc::sysctl(
                mib.as_mut_ptr(),
                2,
                &mut cpus as *mut _ as *mut _,
                &mut cpus_size as *mut _ as *mut _,
                ptr::null_mut(),
                0,
            )
        };

        if res == -1 {
            return Err(io::Error::last_os_error());
        } else if cpus == 0 {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "The number of hardware threads is not known for the target platform",
            ));
        }
    }
    Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
}

#[cfg(any(
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    target_os = "ios",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    #[cfg(any(target_os = "android", target_os = "linux"))]
    {
        let mut set: libc::cpu_set_t = unsafe { core::mem::zeroed() };
        if unsafe { libc::sched_getaffinity(0, core::mem::size_of::<libc::cpu_set_t>(), &mut set) }
            == 0
        {
            let count = unsafe { libc::CPU_COUNT(&set) };
            return Ok(unsafe { NonZeroUsize::new_unchecked(count as usize) });
        }
    }
    match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
        -1 => Err(io::Error::last_os_error()),
        0 => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        )),
        cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
    }
}

#[cfg(target_os = "openbsd")]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    use crate::ptr;

    let mut cpus: libc::c_uint = 0;
    let mut cpus_size = crate::mem::size_of_val(&cpus);
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

    let res = unsafe {
        libc::sysctl(
            mib.as_mut_ptr(),
            2,
            &mut cpus as *mut _ as *mut _,
            &mut cpus_size as *mut _ as *mut _,
            ptr::null_mut(),
            0,
        )
    };

    if res == -1 {
        Err(io::Error::last_os_error())
    } else if cpus == 0 {
        Err(io::Error::new(
            io::ErrorKind::NotFound,
            "The number of hardware threads is not known for the target platform",
        ))
    } else {
        Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
    }
}

#[cfg(target_os = "haiku")]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    unsafe {
        let mut sinfo: libc::system_info = crate::mem::zeroed();
        let res = libc::get_system_info(&mut sinfo);

        if res != libc::B_OK {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "The number of hardware threads is not known for the target platform",
            ));
        }

        Ok(NonZeroUsize::new_unchecked(sinfo.cpu_count as usize))
    }
}

#[cfg(not(any(
    windows,
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "android",
    target_os = "emscripten",
    target_os = "fuchsia",
    target_os = "ios",
    target_os = "linux",
    target_os = "macos",
    target_os = "solaris",
    target_os = "illumos",
    target_os = "openbsd",
    target_os = "haiku",
)))]
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
    Err(io::Error::new(
        io::ErrorKind::Unsupported,
        "Getting the number of hardware threads is not supported on the target platform",
    ))
}
<|endoftext|>


<commit_before>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! enum_string {
    ($name:ident {
        $($variant:ident = $value:expr,)*
    }) => {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        #[repr(u32)]
        pub enum $name {
            $($variant,)*
        }

        impl ::serde::de::Deserialize for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
                where D: ::serde::de::Deserializer
            {
                struct Visitor;
                impl ::serde::de::Visitor for Visitor {
                    type Value = $name;
                    fn expecting(&self, formatter: &mut ::std::fmt::Formatter)
                                 -> ::std::fmt::Result
                    {
                        $(
                            let _ = formatter.write_str(concat!($value, "\n"))?;
                        )*
                            Ok(())
                    }

                    fn visit_str<E>(self, value: &str)-> Result<Self::Value, E>
                        where E: ::serde::de::Error
                    {
                        match value {
                            $($value => Ok($name::$variant),)*
                                bad => {
                                let msg = format!("invalid value: {}", bad);
                                    Err(E::custom(msg))
                                },
                        }
                    }
                }
                deserializer.deserialize_str(Visitor)
            }

        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::ser::Serializer
            {
                match *self {
                    $( $name::$variant => serializer.serialize_str($value), )*
                }
            }
        }
    }
}

macro_rules! enum_number {
    ($name:ident { $($variant:ident = $value:expr, )* }) => {
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        #[allow(non_camel_case_types)]
        #[repr(u32)]
        pub enum $name {
            $($variant = $value,)*
        }

        impl ::serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::Serializer
            {
                serializer.serialize_u64(*self as u64)
            }
        }

        impl ::serde::Deserialize for $name {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where D: ::serde::Deserializer
            {
                struct Visitor;
                impl ::serde::de::Visitor for Visitor {
                    type Value = $name;

                    fn expecting(&self, formatter: &mut ::std::fmt::Formatter)
                                 -> ::std::fmt::Result
                    {
                        formatter.write_str("GLenum")
                    }

                    fn visit_u64<E>(self, value: u64) -> Result<$name, E>
                        where E: ::serde::de::Error
                    {
                        match value {
                            $( $value => Ok($name::$variant), )*
                            _ => Err(E::custom(
                                format!("unknown {} value: {}",
                                stringify!($name), value))),
                        }
                    }
                }
                deserializer.deserialize_u64(Visitor)
            }
        }
    }
}
<commit_msg>impl Into<u32> for enum_*!<commit_after>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! enum_string {
    ($name:ident {
        $($variant:ident = $value:expr,)*
    }) => {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        #[repr(u32)]
        pub enum $name {
            $($variant,)*
        }

        impl Into<u32> for $name {
            fn into(self) -> u32 {
                self as u32
            }
        }

        impl ::serde::de::Deserialize for $name {
            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
                where D: ::serde::de::Deserializer
            {
                struct Visitor;
                impl ::serde::de::Visitor for Visitor {
                    type Value = $name;
                    fn expecting(&self, formatter: &mut ::std::fmt::Formatter)
                                 -> ::std::fmt::Result
                    {
                        $(
                            let _ = formatter.write_str(concat!($value, "\n"))?;
                        )*
                            Ok(())
                    }

                    fn visit_str<E>(self, value: &str)-> Result<Self::Value, E>
                        where E: ::serde::de::Error
                    {
                        match value {
                            $($value => Ok($name::$variant),)*
                                bad => {
                                let msg = format!("invalid value: {}", bad);
                                    Err(E::custom(msg))
                                },
                        }
                    }
                }
                deserializer.deserialize_str(Visitor)
            }

        }

        impl ::serde::ser::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::ser::Serializer
            {
                match *self {
                    $( $name::$variant => serializer.serialize_str($value), )*
                }
            }
        }
    }
}

macro_rules! enum_number {
    ($name:ident { $($variant:ident = $value:expr, )* }) => {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        #[repr(u32)]
        pub enum $name {
            $($variant = $value,)*
        }

        impl Into<u32> for $name {
            fn into(self) -> u32 {
                self as u32
            }
        }

        impl ::serde::Serialize for $name {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where S: ::serde::Serializer
            {
                serializer.serialize_u64(*self as u64)
            }
        }

        impl ::serde::Deserialize for $name {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where D: ::serde::Deserializer
            {
                struct Visitor;
                impl ::serde::de::Visitor for Visitor {
                    type Value = $name;

                    fn expecting(&self, formatter: &mut ::std::fmt::Formatter)
                                 -> ::std::fmt::Result
                    {
                        formatter.write_str("GLenum")
                    }

                    fn visit_u64<E>(self, value: u64) -> Result<$name, E>
                        where E: ::serde::de::Error
                    {
                        match value {
                            $( $value => Ok($name::$variant), )*
                            _ => Err(E::custom(
                                format!("unknown {} value: {}",
                                stringify!($name), value))),
                        }
                    }
                }
                deserializer.deserialize_u64(Visitor)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher, SipHasher};

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;
    fn is_goal(&self, state: &Self::State) -> bool;
}

struct HashTracker<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

impl<T> HashTracker<T> where T: Hash {
    fn new() -> HashTracker<T> {
        HashTracker {
            hash_set: HashSet::new(),
            phantom: PhantomData
        }
    }

    fn contains(&mut self, item: &T) -> bool {
        let mut hasher = SipHasher::new();
        item.hash(&mut hasher);
        let hash = hasher.finish();
        let contains = self.hash_set.contains(&hash);
        if !contains {
            self.hash_set.insert(hash);
        }
        contains
    }
}

pub fn depth_first_search<S>(space: &S, start: S::State) -> Option<Vec<S::Action>>
where S: SearchSpace {
    if space.is_goal(&start) {
        return Some(vec![]);
    }

    let mut path = Vec::new();
    let mut visited = HashTracker::new();
    let mut frontier = vec![space.expand(&start)];

    loop {
        let result = match frontier.last_mut() {
            None => {
                return None
            },
            Some(&mut ref mut iter) => {
                match iter.next() {
                    None => {
                        path.pop();
                        None
                    },
                    Some((action, state)) => {
                        if visited.contains(&state) {
                            continue;
                        }
                        path.push(action);
                        if space.is_goal(&state) {
                            return Some(path);
                        }
                        Some(space.expand(&state))
                    }
                }
            }
        };
        match result {
            None => {
                frontier.pop();
            }
            Some(iter) => {
                frontier.push(iter);
            }
        };
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::*;

    #[test]
    pub fn test_depth_first_search() {
        struct S {
            goal: i32,
        }

        #[derive(Debug, PartialEq)]
        enum Direction {
            Left,
            Right
        }

        impl SearchSpace for S {
            type State = i32;
            type Action = Direction;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Direction::Left, 1), (Direction::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Direction::Left, 3), (Direction::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Direction::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }

            fn is_goal(&self, state: &i32) -> bool {
                return self.goal == *state
            }
        }

        impl S {
            fn solve(start: i32, goal: i32) -> Option<Vec<Direction>> {
                let space = S { goal: goal };
                depth_first_search(&space, start)
            }
        }

        assert_eq!(S::solve(0, 0).unwrap(), vec![]);
        assert_eq!(S::solve(0, 1).unwrap(), vec![Direction::Left]);
        assert_eq!(S::solve(0, 2).unwrap(), vec![Direction::Right]);
        assert_eq!(S::solve(0, 3).unwrap(), vec![Direction::Left, Direction::Left]);
        assert_eq!(S::solve(0, 4).unwrap(), vec![Direction::Left, Direction::Right]);
        assert_eq!(S::solve(2, 2).unwrap(), vec![]);
        assert!(S::solve(2, 0).is_none());
        assert!(S::solve(5, 0).is_none());
    }
}
<commit_msg>Further refactoring and simplification; introduce search goals; dfs is a method<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher, SipHasher};

struct HashTracker<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

impl<T> HashTracker<T> where T: Hash {
    fn new() -> HashTracker<T> {
        HashTracker {
            hash_set: HashSet::new(),
            phantom: PhantomData
        }
    }

    fn contains(&mut self, item: &T) -> bool {
        let mut hasher = SipHasher::new();
        item.hash(&mut hasher);
        let hash = hasher.finish();
        let contains = self.hash_set.contains(&hash);
        if !contains {
            self.hash_set.insert(hash);
        }
        contains
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut path = Vec::new();
        let mut visited = HashTracker::new();
        let mut frontier = vec![self.expand(&start)];

        loop {
            let result = match frontier.last_mut() {
                None => {
                    return None
                },
                Some(&mut ref mut iter) => {
                    match iter.next() {
                        None => {
                            path.pop();
                            None
                        },
                        Some((action, state)) => {
                            if visited.contains(&state) {
                                continue;
                            }
                            path.push(action);
                            if goal.is_goal(&state) {
                                return Some(path);
                            }
                            Some(self.expand(&state))
                        }
                    }
                }
            };
            match result {
                None => {
                    frontier.pop();
                }
                Some(iter) => {
                    frontier.push(iter);
                }
            };
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<|endoftext|>