<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/. */

#![feature(box_syntax)]
#![feature(link_args)]

#[macro_use]
extern crate objc;

#[macro_use]
extern crate log;

extern crate libc;
extern crate cocoa;
extern crate gleam;
extern crate objc_foundation;
extern crate open;

mod app;
mod window;
mod view;
mod servo;
mod platform;
mod state;

use app::{App, AppEvent, AppCommand};
use window::{Window, WindowEvent, WindowCommand};
use view::ViewEvent;
use servo::ServoEvent;
use std::rc::Rc;
use std::env::args;
use servo::{Servo, ServoUrl, WebRenderDebugOption};

use platform::get_state;

fn main() {

    let logs = platform::Logger::init();

    info!("starting");

    platform::init();

    let app = App::load().unwrap();
    let window = app.create_window().unwrap();

    get_state().current_window_index = Some(0);
    get_state().window_states.push(Window::get_init_state());

    let view = Rc::new(window.create_view().unwrap());

    Servo::configure().unwrap();
    let servo = {
        let geometry = view.get_geometry();
        let waker = window.create_event_loop_waker();
        Servo::new(geometry, view.clone(), waker)
    };

    // Skip first argument (executable), and find the first
    // argument that doesn't start with `-`
    let url = args().skip(1).find(|arg| {
        !arg.starts_with("-")
    }).unwrap_or("https://blog.servo.org/".to_owned());

    let browser = servo.create_browser(&url);
    servo.select_browser(browser.id);

    get_state().window_states[0].current_browser_index = Some(0);
    get_state().window_states[0].browser_states.push(browser);

    info!("Servo version: {}", servo.version());

    let handle_events = || {

        // Loop until no events are available anymore.
        loop {

            let app_events = app.get_events();
            let win_events = window.get_events();
            let view_events = view.get_events();
            let servo_events = servo.get_events();

            if app_events.is_empty() &&
               win_events.is_empty() &&
               view_events.is_empty() &&
               servo_events.is_empty() {
                   break
            }

            // FIXME: it's really annoying we need this
            let mut force_sync = false;
            let mut ui_invalidated = false;

            for event in app_events {
                match event {
                    AppEvent::DidFinishLaunching => {
                        // FIXME: does this work?
                    }
                    AppEvent::WillTerminate => {
                        // FIXME: does this work?
                    }
                    AppEvent::DidChangeScreenParameters => {
                        // FIXME: does this work?
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    AppEvent::DoCommand(cmd) => {
                        match cmd {
                            AppCommand::ClearHistory => {
                                // FIXME
                            }
                            AppCommand::ToggleOptionDarkTheme => {
                                ui_invalidated = true;
                                get_state().dark_theme = !get_state().dark_theme;
                                window.update_theme();
                            }
                        }
                    }
                }
            }

            for event in win_events {
                match event {
                    WindowEvent::EventLoopAwaken => {
                        force_sync = true;
                    }
                    WindowEvent::GeometryDidChange => {
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    WindowEvent::DidEnterFullScreen => {
                        // FIXME
                    }
                    WindowEvent::DidExitFullScreen => {
                        // FIXME
                    }
                    WindowEvent::WillClose => {
                        // FIXME
                    }
                    WindowEvent::DoCommand(cmd) => {
                        let idx = get_state().window_states[0].current_browser_index.unwrap();
                        let ref mut state = get_state().window_states[0].browser_states[idx];
                        match cmd {
                            WindowCommand::Stop => {
                                // FIXME
                            }
                            WindowCommand::Reload => {
                                servo.reload(state.id);
                            }
                            WindowCommand::NavigateBack => {
                                servo.go_back(state.id);
                            }
                            WindowCommand::NavigateForward => {
                                servo.go_forward(state.id);
                            }
                            WindowCommand::OpenLocation => {
                                window.focus_urlbar();
                            }
                            WindowCommand::OpenInDefaultBrowser => {
                                if let Some(ref url) = state.url {
                                    open::that(url.clone()).ok();
                                }
                            }
                            WindowCommand::ToggleSidebar => {
                                window.toggle_sidebar();
                            }
                            WindowCommand::ZoomIn => {
                                ui_invalidated = true;
                                state.zoom *= 1.1;
                                servo.zoom(state.zoom);
                            }
                            WindowCommand::ZoomOut => {
                                ui_invalidated = true;
                                state.zoom /= 1.1;
                                servo.zoom(state.zoom);
                            }
                            WindowCommand::ZoomToActualSize => {
                                ui_invalidated = true;
                                state.zoom = 1.0;
                                servo.reset_zoom();
                            }
                            WindowCommand::ShowOptions => {
                                window.show_options();
                            }
                            WindowCommand::Load(request) => {
                                state.user_input = Some(request.clone());
                                let url = ServoUrl::parse(&request).or_else(|error| {
                                    // FIXME: weak
                                    if request.ends_with(".com") || request.ends_with(".org") || request.ends_with(".net") {
                                        ServoUrl::parse(&format!("http://{}", request))
                                    } else {
                                        Err(error)
                                    }
                                }).or_else(|_| {
                                    ServoUrl::parse(&format!("https://duckduckgo.com/html/?q={}", request))
                                });
                                match url {
                                    Ok(url) => {
                                        servo.load_url(state.id, url)
                                    },
                                    Err(err) => warn!("Can't parse url: {}", err),
                                }
                            }
                            WindowCommand::ToggleOptionShowLogs => {
                                get_state().window_states[0].logs_visible = !get_state().window_states[0].logs_visible;
                                ui_invalidated = true;
                            },
                            WindowCommand::NewTab => {
                                let browser = servo.create_browser("about:blank");
                                servo.select_browser(browser.id);
                                servo.update_geometry(view.get_geometry());
                                get_state().window_states[0].current_browser_index = Some(idx + 1);
                                get_state().window_states[0].browser_states.push(browser);
                                ui_invalidated = true;
                            },
                            WindowCommand::CloseTab => {
                                if get_state().window_states[0].browser_states.len() > 1 {
                                    let id = get_state().window_states[0].browser_states[idx].id;
                                    servo.close_browser(id);
                                    let new_id = if idx == get_state().window_states[0].browser_states.len() - 1 {
                                        get_state().window_states[0].current_browser_index = Some(idx - 1);
                                        get_state().window_states[0].browser_states[idx - 1].id
                                    } else {
                                        get_state().window_states[0].browser_states[idx + 1].id
                                    };
                                    servo.select_browser(new_id);
                                    get_state().window_states[0].browser_states.remove(idx);
                                    ui_invalidated = true;
                                }
                            },
                            WindowCommand::PrevTab => {
                                let new_idx = if idx == 0 {
                                    get_state().window_states[0].browser_states.len() - 1
                                } else {
                                    idx - 1
                                };
                                get_state().window_states[0].current_browser_index = Some(new_idx);
                                let id = get_state().window_states[0].browser_states[new_idx].id;
                                servo.select_browser(id);
                                ui_invalidated = true;
                            },
                            WindowCommand::NextTab => {
                                let new_idx = if idx == get_state().window_states[0].browser_states.len() - 1 {
                                    0
                                } else {
                                    idx + 1
                                };
                                get_state().window_states[0].current_browser_index = Some(new_idx);
                                let id = get_state().window_states[0].browser_states[new_idx].id;
                                servo.select_browser(id);
                                ui_invalidated = true;
                            },
                            WindowCommand::SelectTab(idx) => {
                                if get_state().window_states[0].current_browser_index != Some(idx) {
                                    get_state().window_states[0].current_browser_index = Some(idx);
                                    let id = get_state().window_states[0].browser_states[idx].id;
                                    servo.select_browser(id);
                                    ui_invalidated = true;
                                }
                            },
                            WindowCommand::ToggleOptionFragmentBorders => { },
                            WindowCommand::ToggleOptionParallelDisplayListBuidling => { },
                            WindowCommand::ToggleOptionShowParallelLayout => { },
                            WindowCommand::ToggleOptionConvertMouseToTouch => { },
                            WindowCommand::ToggleOptionTileBorders => { },

                            WindowCommand::ToggleOptionWRProfiler => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_profiler = !state.debug_options.wr_profiler;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::Profiler);
                            },

                            WindowCommand::ToggleOptionWRTextureCacheDebug => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_texture_cache_debug = !state.debug_options.wr_texture_cache_debug;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::TextureCacheDebug);
                            },

                            WindowCommand::ToggleOptionWRTargetDebug => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_render_target_debug = !state.debug_options.wr_render_target_debug;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::RenderTargetDebug);
                            },
                        }
                    }
                }
            }

            for event in view_events {
                let idx = get_state().window_states[0].current_browser_index.unwrap();
                let ref mut state = get_state().window_states[0].browser_states[idx];
                match event {
                    ViewEvent::GeometryDidChange => {
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    ViewEvent::MouseWheel(delta, phase) => {
                        let (x, y) = match delta {
                            view::MouseScrollDelta::PixelDelta(x, y) => {
                                (x, y)
                            },
                            _ => (0.0, 0.0),
                        };
                        servo.perform_scroll(0, 0, x, y, phase);
                    }
                    ViewEvent::MouseMoved(x, y) => {
                        state.last_mouse_point = (x, y);
                        servo.perform_mouse_move(x, y);
                    }
                    ViewEvent::MouseInput(element_state, button) => {
                        let (x, y) = state.last_mouse_point;
                        let (org_x, org_y) = state.last_mouse_down_point;
                        let last_mouse_down_button = state.last_mouse_down_button;
                        servo.perform_click(x, y, org_x, org_y, element_state, button, last_mouse_down_button);
                        state.last_mouse_down_point = (x, y);
                        if element_state == view::ElementState::Pressed {
                            state.last_mouse_down_button = Some(button);
                        }
                    }
                    ViewEvent::KeyEvent(c, key, keystate, modifiers) => {
                        servo.send_key(state.id, c, key, keystate, modifiers);
                    }
                }
            }

            for event in servo_events {
                match event {
                    ServoEvent::SetWindowInnerSize(..) => {
                        // ignore
                    }
                    ServoEvent::SetWindowPosition(..) => {
                        // ignore
                    }
                    ServoEvent::SetFullScreenState(fullscreen) => {
                        if fullscreen {
                            view.enter_fullscreen();
                        } else {
                            view.exit_fullscreen();
                        }
                    }
                    ServoEvent::TitleChanged(id, title) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.title = title;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::StatusChanged(status) => {
                        window.set_status(status);
                    }
                    ServoEvent::LoadStart(id) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.is_loading = true;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::LoadEnd(id) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.is_loading = false;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::HeadParsed(..) => {
                        // FIXME
                    }
                    ServoEvent::HistoryChanged(id, entries, current) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                let url = entries[current].url.to_string();
                                browser.url = Some(url);
                                browser.can_go_back = current > 0;
                                browser.can_go_forward = current < entries.len() - 1;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::CursorChanged(cursor) => {
                        window.set_cursor(cursor);
                    }
                    ServoEvent::FaviconChanged(..) => {
                        // FIXME
                    }
                    ServoEvent::Key(..) => {
                        // FIXME
                    }
                }
            }

            if ui_invalidated {
                app.state_changed();
                window.state_changed();
            }

            servo.sync(force_sync);
        }

        // Here, only stuff that we know for sure won't trigger any
        // new events

        // FIXME: logs will grow until pulled
        if get_state().window_states[0].logs_visible {
            window.append_logs(&logs.get_logs());
        }
    };

    view.set_live_resize_callback(&handle_events);

    app.run(handle_events);

}
<commit_msg>select next browser before closing current one<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/. */

#![feature(box_syntax)]
#![feature(link_args)]

#[macro_use]
extern crate objc;

#[macro_use]
extern crate log;

extern crate libc;
extern crate cocoa;
extern crate gleam;
extern crate objc_foundation;
extern crate open;

mod app;
mod window;
mod view;
mod servo;
mod platform;
mod state;

use app::{App, AppEvent, AppCommand};
use window::{Window, WindowEvent, WindowCommand};
use view::ViewEvent;
use servo::ServoEvent;
use std::rc::Rc;
use std::env::args;
use servo::{Servo, ServoUrl, WebRenderDebugOption};

use platform::get_state;

fn main() {

    let logs = platform::Logger::init();

    info!("starting");

    platform::init();

    let app = App::load().unwrap();
    let window = app.create_window().unwrap();

    get_state().current_window_index = Some(0);
    get_state().window_states.push(Window::get_init_state());

    let view = Rc::new(window.create_view().unwrap());

    Servo::configure().unwrap();
    let servo = {
        let geometry = view.get_geometry();
        let waker = window.create_event_loop_waker();
        Servo::new(geometry, view.clone(), waker)
    };

    // Skip first argument (executable), and find the first
    // argument that doesn't start with `-`
    let url = args().skip(1).find(|arg| {
        !arg.starts_with("-")
    }).unwrap_or("https://blog.servo.org/".to_owned());

    let browser = servo.create_browser(&url);
    servo.select_browser(browser.id);

    get_state().window_states[0].current_browser_index = Some(0);
    get_state().window_states[0].browser_states.push(browser);

    info!("Servo version: {}", servo.version());

    let handle_events = || {

        // Loop until no events are available anymore.
        loop {

            let app_events = app.get_events();
            let win_events = window.get_events();
            let view_events = view.get_events();
            let servo_events = servo.get_events();

            if app_events.is_empty() &&
               win_events.is_empty() &&
               view_events.is_empty() &&
               servo_events.is_empty() {
                   break
            }

            // FIXME: it's really annoying we need this
            let mut force_sync = false;
            let mut ui_invalidated = false;

            for event in app_events {
                match event {
                    AppEvent::DidFinishLaunching => {
                        // FIXME: does this work?
                    }
                    AppEvent::WillTerminate => {
                        // FIXME: does this work?
                    }
                    AppEvent::DidChangeScreenParameters => {
                        // FIXME: does this work?
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    AppEvent::DoCommand(cmd) => {
                        match cmd {
                            AppCommand::ClearHistory => {
                                // FIXME
                            }
                            AppCommand::ToggleOptionDarkTheme => {
                                ui_invalidated = true;
                                get_state().dark_theme = !get_state().dark_theme;
                                window.update_theme();
                            }
                        }
                    }
                }
            }

            for event in win_events {
                match event {
                    WindowEvent::EventLoopAwaken => {
                        force_sync = true;
                    }
                    WindowEvent::GeometryDidChange => {
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    WindowEvent::DidEnterFullScreen => {
                        // FIXME
                    }
                    WindowEvent::DidExitFullScreen => {
                        // FIXME
                    }
                    WindowEvent::WillClose => {
                        // FIXME
                    }
                    WindowEvent::DoCommand(cmd) => {
                        let idx = get_state().window_states[0].current_browser_index.unwrap();
                        let ref mut state = get_state().window_states[0].browser_states[idx];
                        match cmd {
                            WindowCommand::Stop => {
                                // FIXME
                            }
                            WindowCommand::Reload => {
                                servo.reload(state.id);
                            }
                            WindowCommand::NavigateBack => {
                                servo.go_back(state.id);
                            }
                            WindowCommand::NavigateForward => {
                                servo.go_forward(state.id);
                            }
                            WindowCommand::OpenLocation => {
                                window.focus_urlbar();
                            }
                            WindowCommand::OpenInDefaultBrowser => {
                                if let Some(ref url) = state.url {
                                    open::that(url.clone()).ok();
                                }
                            }
                            WindowCommand::ToggleSidebar => {
                                window.toggle_sidebar();
                            }
                            WindowCommand::ZoomIn => {
                                ui_invalidated = true;
                                state.zoom *= 1.1;
                                servo.zoom(state.zoom);
                            }
                            WindowCommand::ZoomOut => {
                                ui_invalidated = true;
                                state.zoom /= 1.1;
                                servo.zoom(state.zoom);
                            }
                            WindowCommand::ZoomToActualSize => {
                                ui_invalidated = true;
                                state.zoom = 1.0;
                                servo.reset_zoom();
                            }
                            WindowCommand::ShowOptions => {
                                window.show_options();
                            }
                            WindowCommand::Load(request) => {
                                state.user_input = Some(request.clone());
                                let url = ServoUrl::parse(&request).or_else(|error| {
                                    // FIXME: weak
                                    if request.ends_with(".com") || request.ends_with(".org") || request.ends_with(".net") {
                                        ServoUrl::parse(&format!("http://{}", request))
                                    } else {
                                        Err(error)
                                    }
                                }).or_else(|_| {
                                    ServoUrl::parse(&format!("https://duckduckgo.com/html/?q={}", request))
                                });
                                match url {
                                    Ok(url) => {
                                        servo.load_url(state.id, url)
                                    },
                                    Err(err) => warn!("Can't parse url: {}", err),
                                }
                            }
                            WindowCommand::ToggleOptionShowLogs => {
                                get_state().window_states[0].logs_visible = !get_state().window_states[0].logs_visible;
                                ui_invalidated = true;
                            },
                            WindowCommand::NewTab => {
                                let browser = servo.create_browser("about:blank");
                                servo.select_browser(browser.id);
                                servo.update_geometry(view.get_geometry());
                                get_state().window_states[0].current_browser_index = Some(idx + 1);
                                get_state().window_states[0].browser_states.push(browser);
                                ui_invalidated = true;
                            },
                            WindowCommand::CloseTab => {
                                if get_state().window_states[0].browser_states.len() > 1 {
                                    let id = get_state().window_states[0].browser_states[idx].id;
                                    let new_id = if idx == get_state().window_states[0].browser_states.len() - 1 {
                                        get_state().window_states[0].current_browser_index = Some(idx - 1);
                                        get_state().window_states[0].browser_states[idx - 1].id
                                    } else {
                                        get_state().window_states[0].browser_states[idx + 1].id
                                    };
                                    servo.select_browser(new_id);
                                    servo.close_browser(id);
                                    get_state().window_states[0].browser_states.remove(idx);
                                    ui_invalidated = true;
                                }
                            },
                            WindowCommand::PrevTab => {
                                let new_idx = if idx == 0 {
                                    get_state().window_states[0].browser_states.len() - 1
                                } else {
                                    idx - 1
                                };
                                get_state().window_states[0].current_browser_index = Some(new_idx);
                                let id = get_state().window_states[0].browser_states[new_idx].id;
                                servo.select_browser(id);
                                ui_invalidated = true;
                            },
                            WindowCommand::NextTab => {
                                let new_idx = if idx == get_state().window_states[0].browser_states.len() - 1 {
                                    0
                                } else {
                                    idx + 1
                                };
                                get_state().window_states[0].current_browser_index = Some(new_idx);
                                let id = get_state().window_states[0].browser_states[new_idx].id;
                                servo.select_browser(id);
                                ui_invalidated = true;
                            },
                            WindowCommand::SelectTab(idx) => {
                                if get_state().window_states[0].current_browser_index != Some(idx) {
                                    get_state().window_states[0].current_browser_index = Some(idx);
                                    let id = get_state().window_states[0].browser_states[idx].id;
                                    servo.select_browser(id);
                                    ui_invalidated = true;
                                }
                            },
                            WindowCommand::ToggleOptionFragmentBorders => { },
                            WindowCommand::ToggleOptionParallelDisplayListBuidling => { },
                            WindowCommand::ToggleOptionShowParallelLayout => { },
                            WindowCommand::ToggleOptionConvertMouseToTouch => { },
                            WindowCommand::ToggleOptionTileBorders => { },

                            WindowCommand::ToggleOptionWRProfiler => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_profiler = !state.debug_options.wr_profiler;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::Profiler);
                            },

                            WindowCommand::ToggleOptionWRTextureCacheDebug => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_texture_cache_debug = !state.debug_options.wr_texture_cache_debug;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::TextureCacheDebug);
                            },

                            WindowCommand::ToggleOptionWRTargetDebug => {
                                let ref mut state = get_state().window_states[0].browser_states[0];
                                state.debug_options.wr_render_target_debug = !state.debug_options.wr_render_target_debug;
                                servo.toggle_webrender_debug_option(WebRenderDebugOption::RenderTargetDebug);
                            },
                        }
                    }
                }
            }

            for event in view_events {
                let idx = get_state().window_states[0].current_browser_index.unwrap();
                let ref mut state = get_state().window_states[0].browser_states[idx];
                match event {
                    ViewEvent::GeometryDidChange => {
                        servo.update_geometry(view.get_geometry());
                        view.update_drawable();
                    }
                    ViewEvent::MouseWheel(delta, phase) => {
                        let (x, y) = match delta {
                            view::MouseScrollDelta::PixelDelta(x, y) => {
                                (x, y)
                            },
                            _ => (0.0, 0.0),
                        };
                        servo.perform_scroll(0, 0, x, y, phase);
                    }
                    ViewEvent::MouseMoved(x, y) => {
                        state.last_mouse_point = (x, y);
                        servo.perform_mouse_move(x, y);
                    }
                    ViewEvent::MouseInput(element_state, button) => {
                        let (x, y) = state.last_mouse_point;
                        let (org_x, org_y) = state.last_mouse_down_point;
                        let last_mouse_down_button = state.last_mouse_down_button;
                        servo.perform_click(x, y, org_x, org_y, element_state, button, last_mouse_down_button);
                        state.last_mouse_down_point = (x, y);
                        if element_state == view::ElementState::Pressed {
                            state.last_mouse_down_button = Some(button);
                        }
                    }
                    ViewEvent::KeyEvent(c, key, keystate, modifiers) => {
                        servo.send_key(state.id, c, key, keystate, modifiers);
                    }
                }
            }

            for event in servo_events {
                match event {
                    ServoEvent::SetWindowInnerSize(..) => {
                        // ignore
                    }
                    ServoEvent::SetWindowPosition(..) => {
                        // ignore
                    }
                    ServoEvent::SetFullScreenState(fullscreen) => {
                        if fullscreen {
                            view.enter_fullscreen();
                        } else {
                            view.exit_fullscreen();
                        }
                    }
                    ServoEvent::TitleChanged(id, title) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.title = title;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::StatusChanged(status) => {
                        window.set_status(status);
                    }
                    ServoEvent::LoadStart(id) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.is_loading = true;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::LoadEnd(id) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                browser.is_loading = false;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::HeadParsed(..) => {
                        // FIXME
                    }
                    ServoEvent::HistoryChanged(id, entries, current) => {
                        match get_state().window_states[0].browser_states.iter_mut().find(|b| b.id == id) {
                            Some(browser) => {
                                let url = entries[current].url.to_string();
                                browser.url = Some(url);
                                browser.can_go_back = current > 0;
                                browser.can_go_forward = current < entries.len() - 1;
                                ui_invalidated = true;
                            }
                            None => { /*FIXME*/ }
                        }
                    }
                    ServoEvent::CursorChanged(cursor) => {
                        window.set_cursor(cursor);
                    }
                    ServoEvent::FaviconChanged(..) => {
                        // FIXME
                    }
                    ServoEvent::Key(..) => {
                        // FIXME
                    }
                }
            }

            if ui_invalidated {
                app.state_changed();
                window.state_changed();
            }

            servo.sync(force_sync);
        }

        // Here, only stuff that we know for sure won't trigger any
        // new events

        // FIXME: logs will grow until pulled
        if get_state().window_states[0].logs_visible {
            window.append_logs(&logs.get_logs());
        }
    };

    view.set_live_resize_callback(&handle_events);

    app.run(handle_events);

}
<|endoftext|>


<commit_before>use instr::Instr;
use cpu::Cpu;
use time::precise_time_ns;
use std::fs::File;
use std::io::Read;

use glium::DisplayBuild;
use glium::Surface;
use glium::SwapBuffersError;
use glium::glutin::Api;
use glium::glutin::GlRequest;
use glium::glutin::Event;

extern crate time;
extern crate getopts;
#[macro_use]
extern crate glium;
extern crate nalgebra;

mod instr;
mod cpu;
mod render;

pub struct AddressSpace {
    data: [u8; 0x10000],
}

impl AddressSpace {

    pub fn new() -> AddressSpace {
        AddressSpace { data: [0; 0x10000] }
    }

    pub fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    pub fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        &self.data[addr as usize .. (addr + bytes) as usize]
    }

    pub fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    pub fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

    pub fn load_rom(&mut self, rom: &mut File) -> std::io::Result<()> {
        // Read in header first
        try!(rom.read(&mut self.data[0x000..0x150]));
        // Then read in remaining cart data
        try!(rom.read(&mut self.data[0x0150..0x8000]));
        Ok(())
    }

}

#[derive(Copy, Clone)]
pub enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy, Clone)]
pub enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

pub struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    pub fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    pub fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    pub fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    pub fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    pub fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    pub fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    pub fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    pub fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    pub fn get_flag(&self, flag: RegFlag) -> bool {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        self.flag & bit != 0
    }

    pub fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

    pub fn set_pc(&mut self, addr: u16) -> u16 {
        let pc = self.pc;
        self.pc = addr;
        pc
    }

    pub fn add_pc(&mut self, n: i8) -> u16 {
        let pc = self.pc;
        self.pc = ((self.pc as i32) + (n as i32)) as u16;
        pc
    }

}

fn main() {
    //  Gather command line args
    let args: Vec<String> = std::env::args().collect();
    let mut opts = getopts::Options::new();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(e) => panic!("Error: {}", e),
    };
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("No input ROM");
        return;
    };

    // Build graphics context and window
    let display = glium::glutin::WindowBuilder::new()
        .with_title("Gameboy Rust".to_string())
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
        .build_glium()
        .unwrap();

    // Do machine initialization
    let mut cpu = Cpu::new();
    {
        let mut ram = cpu.get_ram();
        let mut romfile = match File::open(std::path::Path::new(&input)) {
            Ok(f) => { f },
            Err(e) => {
                println!("Error opening file: {}", e);
                return;
            }
        };
        if let Err(e) = ram.load_rom(&mut romfile) {
            println!("Error loading rom data: {}", e);
            return;
        }
    }

    // Initialize virtual LCD
    let mut lcd = render::GbDisplay::new(&display);

    let mut viewport = {
        let window = display.get_window();
        let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
        render::calculate_viewport(width, height)
    };

    // Simulate CPU
    'main: loop {
        // Collect user input
        for ev in display.poll_events() {
            match ev {
                Event::Closed => {
                    break 'main;
                },
                Event::Resized(..) => {
                    let window = display.get_window();
                    let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
                    viewport = render::calculate_viewport(width, height);
                },
                _ => (),
            }
        }

        // Redraw screen
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        lcd.clear_viewport(&mut target, viewport, (1.0, 1.0, 1.0, 1.0));
        match target.finish().err() {
            Some(SwapBuffersError::ContextLost) => {
                panic!("OpenGL contetxt lost!");
            },
            Some(SwapBuffersError::AlreadySwapped) => {
                println!("Warning: OpenGL buffer already swapped");
            },
            None => (),
        }
    }
}
<commit_msg>Add utility methods to AddressSpace<commit_after>use instr::Instr;
use cpu::Cpu;
use time::precise_time_ns;
use std::fs::File;
use std::io::Read;
use std::ops::Index;

use glium::DisplayBuild;
use glium::Surface;
use glium::SwapBuffersError;
use glium::glutin::Api;
use glium::glutin::GlRequest;
use glium::glutin::Event;

extern crate time;
extern crate getopts;
#[macro_use]
extern crate glium;
extern crate nalgebra;

mod instr;
mod cpu;
mod render;

#[derive(Copy, Clone)]
pub enum MemSection {
    Vram,
    RomBank0,
}

pub struct AddressSpace {
    data: [u8; 0x10000],
}

impl AddressSpace {

    pub fn new() -> AddressSpace {
        AddressSpace { data: [0; 0x10000] }
    }

    pub fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    pub fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        &self.data[addr as usize .. (addr + bytes) as usize]
    }

    pub fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    pub fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

    pub fn load_rom(&mut self, rom: &mut File) -> std::io::Result<()> {
        // Read in header first
        try!(rom.read(&mut self.data[0x000..0x150]));
        // Then read in remaining cart data
        try!(rom.read(&mut self.data[0x0150..0x8000]));
        Ok(())
    }

}

impl Index<u16> for AddressSpace {
    type Output = u8;

    fn index(&self, idx: u16) -> &u8 {
        &self.data[idx as usize]
    }
}

#[derive(Copy, Clone)]
pub enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy, Clone)]
pub enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

pub struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    pub fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    pub fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    pub fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    pub fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    pub fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    pub fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    pub fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    pub fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    pub fn get_flag(&self, flag: RegFlag) -> bool {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        self.flag & bit != 0
    }

    pub fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

    pub fn set_pc(&mut self, addr: u16) -> u16 {
        let pc = self.pc;
        self.pc = addr;
        pc
    }

    pub fn add_pc(&mut self, n: i8) -> u16 {
        let pc = self.pc;
        self.pc = ((self.pc as i32) + (n as i32)) as u16;
        pc
    }

}

fn main() {
    //  Gather command line args
    let args: Vec<String> = std::env::args().collect();
    let mut opts = getopts::Options::new();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(e) => panic!("Error: {}", e),
    };
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("No input ROM");
        return;
    };

    // Build graphics context and window
    let display = glium::glutin::WindowBuilder::new()
        .with_title("Gameboy Rust".to_string())
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
        .build_glium()
        .unwrap();

    // Do machine initialization
    let mut cpu = Cpu::new();
    {
        let mut ram = cpu.get_ram();
        let mut romfile = match File::open(std::path::Path::new(&input)) {
            Ok(f) => { f },
            Err(e) => {
                println!("Error opening file: {}", e);
                return;
            }
        };
        if let Err(e) = ram.load_rom(&mut romfile) {
            println!("Error loading rom data: {}", e);
            return;
        }
    }

    // Initialize virtual LCD
    let mut lcd = render::GbDisplay::new(&display);

    let mut viewport = {
        let window = display.get_window();
        let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
        render::calculate_viewport(width, height)
    };

    // Simulate CPU
    'main: loop {
        // Collect user input
        for ev in display.poll_events() {
            match ev {
                Event::Closed => {
                    break 'main;
                },
                Event::Resized(..) => {
                    let window = display.get_window();
                    let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
                    viewport = render::calculate_viewport(width, height);
                },
                _ => (),
            }
        }

        // Redraw screen
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        lcd.clear_viewport(&mut target, viewport, (1.0, 1.0, 1.0, 1.0));
        match target.finish().err() {
            Some(SwapBuffersError::ContextLost) => {
                panic!("OpenGL contetxt lost!");
            },
            Some(SwapBuffersError::AlreadySwapped) => {
                println!("Warning: OpenGL buffer already swapped");
            },
            None => (),
        }
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate html5ever;
extern crate tendril;

use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;

use std::io::Read;
use hyper::client::Client;

use tendril::*;
use tendril::fmt::{UTF8};

use html5ever::tokenizer::{TokenSink, Token, TokenizerOpts, ParseError};
use html5ever::tokenizer::{TagToken, StartTag, Tag};
use html5ever::driver::{tokenize_to, one_input};

struct LinkFinder{
    links: Vec<String>
}

impl TokenSink for LinkFinder {
    fn process_token(&mut self, token: Token) {
        match token {
            TagToken(tag @ Tag{kind: StartTag, ..}) => {
                if tag.name.as_slice() == "a" {
                    for attr in tag.attrs {
                        if attr.name.local.as_slice() == "href" {
                            self.links.push(String::from(attr.value));
                        }
                    }
                }
            }
            _ => ()
        }
    }
}

fn main() {
    let client = Client::new();

    let res = client.get("http://rust-lang.org").send();

    let mut response = match res {
        Ok(x) => x,
        Err(err) => panic!("{:?}", err)
    };

    let mut body = String::new();

    response.read_to_string(&mut body);

    let mut sink = LinkFinder{links: vec![]};

    sink = tokenize_to(sink, one_input(Tendril::from(body)), Default::default());

    let l = sink.links;

    let data = Arc::new(Mutex::new(l));

    let mut handlers = vec![];

    for i in 0..5 {
        let data = data.clone();
        handlers.push(thread::spawn(move || {
            loop {
                let link = {
                    data.lock().unwrap().pop()
                };
                println!("Starting to download this at thread {}", i); TODO actually download
                thread::sleep_ms(2000);
                match link {
                    Some(d) => println!("Finished downloading {} at thread {}", d, i),
                    None => break
                }
            }
        }));
    }

    for handler in handlers {
        handler.join();
    }

}
<commit_msg>Fix typo<commit_after>extern crate hyper;
extern crate html5ever;
extern crate tendril;

use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;

use std::io::Read;
use hyper::client::Client;

use tendril::*;
use tendril::fmt::{UTF8};

use html5ever::tokenizer::{TokenSink, Token, TokenizerOpts, ParseError};
use html5ever::tokenizer::{TagToken, StartTag, Tag};
use html5ever::driver::{tokenize_to, one_input};

struct LinkFinder{
    links: Vec<String>
}

impl TokenSink for LinkFinder {
    fn process_token(&mut self, token: Token) {
        match token {
            TagToken(tag @ Tag{kind: StartTag, ..}) => {
                if tag.name.as_slice() == "a" {
                    for attr in tag.attrs {
                        if attr.name.local.as_slice() == "href" {
                            self.links.push(String::from(attr.value));
                        }
                    }
                }
            }
            _ => ()
        }
    }
}

fn main() {
    let client = Client::new();

    let res = client.get("http://rust-lang.org").send();

    let mut response = match res {
        Ok(x) => x,
        Err(err) => panic!("{:?}", err)
    };

    let mut body = String::new();

    response.read_to_string(&mut body);

    let mut sink = LinkFinder{links: vec![]};

    sink = tokenize_to(sink, one_input(Tendril::from(body)), Default::default());

    let l = sink.links;

    let data = Arc::new(Mutex::new(l));

    let mut handlers = vec![];

    for i in 0..5 {
        let data = data.clone();
        handlers.push(thread::spawn(move || {
            loop {
                let link = {
                    data.lock().unwrap().pop()
                };
                println!("Starting to download this at thread {}", i);
                // TODO actually download
                thread::sleep_ms(2000);
                match link {
                    Some(d) => println!("Finished downloading {} at thread {}", d, i),
                    None => break
                }
            }
        }));
    }

    for handler in handlers {
        handler.join();
    }

}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate fps_clock;

use sdl2::video::Window;
use sdl2::render::Canvas;
use sdl2::EventPump;
use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use fps_clock::FpsClock;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
    Up,
    Down,
    Left,
    Right
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tile {
    Wall,
    Player,
    PlayerOnGoal,
    Star,
    StarOnGoal,
    Goal,
    Floor,
}
impl Tile {
    /// Character to tile mapping taken from http://sokobano.de/wiki/index.php?title=Level_format
    /// I renamed Box to Star to not conflict with the Box type.
    fn from_char(c: char) -> Result<Tile, String> {
        match c {
            '#' => Ok(Tile::Wall),
            '@' => Ok(Tile::Player),
            '+' => Ok(Tile::PlayerOnGoal),
            '$' => Ok(Tile::Star),
            '*' => Ok(Tile::StarOnGoal),
            '.' => Ok(Tile::Goal),
            ' ' => Ok(Tile::Floor),
            _ => Err(format!("'{}' is an invalid tile", c)),
        }
    }
    fn to_char(&self) -> char {
        use Tile::*;
        match *self {
            Wall => '#',
            Player => '@',
            PlayerOnGoal => '+',
            Star => '$',
            StarOnGoal => '*',
            Goal => '.',
            Floor => ' ',
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Position {
    x: usize,
    y: usize,
}
impl Position {
    fn new(x: usize, y: usize) -> Position {
        Position {x, y}
    }
    fn move_in_direction(&self, dir: Direction) -> Position {
        match dir {
            Direction::Down => Position {y: self.y+1, ..*self},
            Direction::Up => Position {y: self.y-1, ..*self},
            Direction::Left => Position {x: self.x-1, ..*self},
            Direction::Right => Position {x: self.x-1, ..*self},
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Level {
    map: Vec<Vec<Tile>>,
    player_pos: Position,
}
impl Level {
    fn from_lines(lines: Vec<&str>) -> Result<Level, String> {
        let longest_line_len = lines.iter().map(|l| l.len()).max().unwrap();
        let mut map = Vec::with_capacity(lines.len());
        let mut player_pos = None;
        for (y, line) in lines.iter().enumerate() {
            let mut row = Vec::with_capacity(line.len());
            for (x, tile) in line.chars().enumerate() {
                row.push(Tile::from_char(tile)?);
                if tile == '@' || tile == '+' {
                    // This tile is the starting position
                    player_pos = Some(Position::new(x, y));
                }
            }
            if line.len() < longest_line_len {
                for _ in 0..(longest_line_len - line.len()) {
                    row.push(Tile::Floor);
                }
            }
            map.push(row);
        }
        Ok(Level { map: map, player_pos: player_pos.unwrap()})
    }
    fn as_string(&self) -> String {
        let mut map = String::new();
        for line in &self.map {
            for tile in line {
                map.push(tile.to_char());
            }
            map.push('\n');
        }
        map
    }
}

fn load_levels(levels: &str) -> Result<Vec<Level>, String> {
    let mut parsed_levels = Vec::new();
    let mut map_lines = Vec::new();
    for line in levels.lines() {
        let line = line.trim_right();
        let line = if let Some(i) = line.find(';') {
            &line[0..i]
        } else {
            line
        };
        if !line.is_empty() {
            map_lines.push(line);
        } else if line.is_empty() && !map_lines.is_empty() {
            parsed_levels.push(Level::from_lines(map_lines)?);
            map_lines = Vec::new();
        }
    }
    Ok(parsed_levels)
}

fn init_sdl(app_name: &str, width: u32, height: u32) -> Result<(Canvas<Window>, EventPump), String> {
    let sdl_context = sdl2::init()?;
    let video_subsystem = sdl_context.video()?;

    let window = video_subsystem.window(app_name, width, height)
        .position_centered()
        .opengl()
        .build()
        .map_err(|e| format!("{}", e))?;

    let canvas = window.into_canvas().build().map_err(|e| format!("{}", e))?;
    let event_pump = sdl_context.event_pump()?;
    Ok((canvas, event_pump))
}

const LEVELS: &'static str = include_str!("../levels.txt");

fn main() {
    let parsed_levels = load_levels(LEVELS).unwrap();
    for level in &parsed_levels[0..5] {
        println!("{}", level.as_string());
    }
    /*
    let (mut canvas, mut event_pump) = init_sdl("Sokoban", 800, 600).unwrap();
    let mut clock = FpsClock::new(60);
    'main: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'main
                },
                _ => {}
            }
        }
        canvas.set_draw_color(Color::RGB(29, 167, 226));
        canvas.clear();
        canvas.present();
        clock.tick();
    }*/
}
<commit_msg>More work on game<commit_after>extern crate sdl2;
extern crate fps_clock;

use sdl2::video::Window;
use sdl2::render::Canvas;
use sdl2::EventPump;
use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::{LoadTexture, INIT_PNG};
use sdl2::rect::Rect;

use fps_clock::FpsClock;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
    Up,
    Down,
    Left,
    Right
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tile {
    Wall,
    Player,
    PlayerOnGoal,
    Star,
    StarOnGoal,
    Goal,
    Floor,
}
impl Tile {
    /// Character to tile mapping taken from http://sokobano.de/wiki/index.php?title=Level_format
    /// I renamed Box to Star to not conflict with the Box type.
    fn from_char(c: char) -> Result<Tile, String> {
        match c {
            '#' => Ok(Tile::Wall),
            '@' => Ok(Tile::Player),
            '+' => Ok(Tile::PlayerOnGoal),
            '$' => Ok(Tile::Star),
            '*' => Ok(Tile::StarOnGoal),
            '.' => Ok(Tile::Goal),
            ' ' => Ok(Tile::Floor),
            _ => Err(format!("'{}' is an invalid tile", c)),
        }
    }
    fn to_char(&self) -> char {
        use Tile::*;
        match *self {
            Wall => '#',
            Player => '@',
            PlayerOnGoal => '+',
            Star => '$',
            StarOnGoal => '*',
            Goal => '.',
            Floor => ' ',
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Position {
    x: usize,
    y: usize,
}
impl Position {
    fn new(x: usize, y: usize) -> Position {
        Position {x, y}
    }
    fn move_in_direction(&self, dir: Direction) -> Position {
        match dir {
            Direction::Down => Position {y: self.y+1, ..*self},
            Direction::Up => Position {y: self.y-1, ..*self},
            Direction::Left => Position {x: self.x-1, ..*self},
            Direction::Right => Position {x: self.x-1, ..*self},
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Player {
    position: Position,
    direction: Direction,
}
impl Player {
    fn new(position: Position, direction: Direction) -> Player {
        Player {
            position,
            direction,
        }
    }
    fn move_in_direction(&self, direction: Direction) -> Player {
        Player::new(self.position.move_in_direction(direction), direction)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Star {
    position: Position
}
impl Star {
    fn new(position: Position) -> Star {
        Star { position }
    }
    fn move_in_direction(&self, direction: Direction) -> Star {
        Star::new(self.position.move_in_direction(direction))
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct GameState {
    player: Player,
    stars: Vec<Star>,
    steps: usize,
}
impl GameState {
    fn new(player: Player, stars: Vec<Star>, steps: usize) -> GameState {
        GameState { player, stars, steps }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Level {
    width: usize,
    height: usize,
    map: Vec<Vec<Tile>>,
    start_state: GameState,
}
impl Level {
    fn from_lines(lines: Vec<&str>) -> Result<Level, String> {
        let longest_line_len = lines.iter()
                                .map(|l| l.len())
                                .max()
                                .ok_or_else(|| "Invalid level: Level is empty")?;
        let mut map = Vec::with_capacity(lines.len());
        let mut stars = Vec::new();
        let mut player_pos = None;
        for (y, line) in lines.iter().enumerate() {
            let mut row = Vec::with_capacity(line.len());
            for (x, tile) in line.chars().enumerate() {
                let tile = Tile::from_char(tile)?;
                row.push(tile);
                if tile == Tile::Player || tile == Tile::PlayerOnGoal {
                    // This tile is the starting position
                    player_pos = Some(Position::new(x, y));
                } else if tile == Tile::Star || tile == Tile::StarOnGoal {
                    stars.push(Star::new(Position::new(x, y)));
                }
            }
            if line.len() < longest_line_len {
                for _ in 0..(longest_line_len - line.len()) {
                    row.push(Tile::Floor);
                }
            }
            map.push(row);
        }
        let start_state = GameState::new(Player::new(player_pos.ok_or_else(|| "Invalid level: Level has no starting position")?, Direction::Left),
                                         stars,
                                         0);
        let height = map.len();
        Ok(Level { map, width: longest_line_len, height, start_state})
    }
    fn as_string(&self) -> String {
        let mut map = String::new();
        for line in &self.map {
            for tile in line {
                map.push(tile.to_char());
            }
            map.push('\n');
        }
        map
    }
}

fn load_levels(levels: &str) -> Result<Vec<Level>, String> {
    let mut parsed_levels = Vec::new();
    let mut map_lines = Vec::new();
    for line in levels.lines() {
        let line = line.trim_right();
        let line = if let Some(i) = line.find(';') {
            &line[0..i]
        } else {
            line
        };
        if !line.is_empty() {
            map_lines.push(line);
        } else if line.is_empty() && !map_lines.is_empty() {
            parsed_levels.push(Level::from_lines(map_lines)?);
            map_lines = Vec::new();
        }
    }
    Ok(parsed_levels)
}

fn init_sdl(app_name: &str, width: u32, height: u32) -> Result<(Canvas<Window>, EventPump), String> {
    let sdl_context = sdl2::init()?;
    let _image_context = sdl2::image::init(INIT_PNG)?;
    let video_subsystem = sdl_context.video()?;

    let window = video_subsystem.window(app_name, width, height)
        .position_centered()
        .opengl()
        .build()
        .map_err(|e| format!("{}", e))?;

    let canvas = window.into_canvas().build().map_err(|e| format!("{}", e))?;
    let event_pump = sdl_context.event_pump()?;
    Ok((canvas, event_pump))
}

macro_rules! rect {
    ($x:expr, $y:expr, $w:expr, $h:expr) => (Rect::new($x as i32, $y as i32, $w as u32, $h as u32))
}

fn rect_at_center_of(pos: Position, w: u32, h: u32) -> Rect {
    let topleft_x = pos.x as u32 - w/2;
    let topleft_y = pos.y as u32 - h/2;
    rect!(topleft_x, topleft_y, w, h)
}

const LEVELS: &'static str = include_str!("../levels.txt");
const SPRITE_PATH: &'static str = "resources/images/sokoban_spritesheet.png";

fn main() {
    let (width, height) = (800, 600);
    let parsed_levels = load_levels(LEVELS).unwrap();
    let (mut canvas, mut event_pump) = init_sdl("Sokoban", width, height).unwrap();
    let texture_creator = canvas.texture_creator();
    let texture = texture_creator.load_texture(SPRITE_PATH).unwrap();
    let center_rect = rect_at_center_of(Position::new((width/2) as usize, (height/2) as usize), width/2, height/2);
    let mut clock = FpsClock::new(60);
    'main: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'main
                },
                _ => {}
            }
        }
        canvas.set_draw_color(Color::RGB(29, 167, 226));
        canvas.clear();
        canvas.copy(&texture, None, Some(center_rect)).expect("Render failed");
        canvas.present();
        clock.tick();
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::UserReply::{
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use app_result::{
   AppResult,
   AppError
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

mod app_result;
mod file_system;
mod file_conflict;
mod parser;
mod user_reply;
mod appdirs;
mod args;

fn main()
{
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(&mut stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();
   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);
      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());
         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(copy(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.\n", dir_str, dir_str);

   Ok(())
}
<commit_msg>Add 'move_file' function<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::UserReply::{
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use app_result::{
   AppResult,
   AppError
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

mod app_result;
mod file_system;
mod file_conflict;
mod parser;
mod user_reply;
mod appdirs;
mod args;

fn main()
{
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(&mut stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();

   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);

      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());

         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(move_file(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

fn move_file(from_file: &Path, to_file: &Path) -> AppResult<()>
{
   try!(copy(from_file, to_file));
   try!(unlink(from_file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.\n", dir_str, dir_str);

   Ok(())
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
//use std::fs::File;

extern crate time;

fn main() {

    // <134>2017-04-24T20:38:26Z cache-iad2645 fastly-logs-5-east[367774]: 54.87.185.35 "-" "GET
    // /npm/public/registry/i/is-utf8/_attachments/doc.json" 200 "npm/4.2.0 node/v7.8.0 linux x64"
    // "install" "26f82920d62b24e9" "HIT" "(null)" "cache-iad2645-IAD" "0" "1016" "1466" "251"

    // only quoted values have spaces.
    let mut value = String::new();
    let mut values = Vec::new();
    let mut state = "out";

    for byte in io::stdin().bytes() {
        let c = byte.unwrap() as char;

        if c == '\n' {
            // if i have a pending value add it to values
            if value.len() > 0 {
                values.push(value)
            }

            format(&values);

            value = String::new();
            values = Vec::new();
            continue;
        }

        if state == "out" {
            if c == '"' {
                state = "quoted";
                // skip quote
                continue;
            } else if c != ' ' {
                state = "value";
            }
        }

        if state == "value" {
            if c == ' ' {
                values.push(value);
                value = String::new();
                state = "out";
            } else if c != '"' {
                value.push(c);
            }
        }

        if state == "quoted" {
            if c == '"' {
                values.push(value);
                value = String::new();
                state = "out";
            } else {
                value.push(c);
            }
        }
    }
}

fn format(values: &Vec<String>) {
    let len = values.len();

    if len == 0 {
        return;
    }

    let date = parse_date(values[0].clone());
    let unixtime = logtime_to_unixtime(&date);

    // invalid time. wont count.
    if unixtime == 0 {
        return;
    }

    let pop = substr(&values[1], 6, 3);

    // if i have any values emit them.

    let service = path_to_service(&values[5]);

    let status = &values[6];

    let mut offset = 0;
    if len == 18 {
        offset = 1;
    }

    let egress_bytes = &values[15 + offset];
    let ingress_bytes = &values[16 + offset];

    for v in values {
        print!("{}, ", v);
    }

    println!("[{}]", len);

    println!("format {} {} {} {} {} {} {}",
             date,
             unixtime,
             pop,
             service,
             status,
             egress_bytes,
             ingress_bytes);
}

fn parse_date(dateref: String) -> String {
    let mut date = dateref.clone();
    let offset_option = date.find('>');

    if offset_option != None {

        let offset = offset_option.unwrap() + 1;
        // -1 to remove the trailing Z
        let end = date.len() - offset - 1;

        //date = date.chars().skip(offset).take(end).collect();
        date = substr(&date, offset, end)
    }
    return date;
}

fn logtime_to_unixtime(date: &String) -> i64 {

    // 2017-04-24T20:38:26
    let unixtime = match time::strptime(date, "%Y-%m-%dT%H:%M:%S") {
        Ok(v) => v.to_timespec().sec,
        _ => 0,
    };

    return unixtime;
}

fn substr(string: &String, start: usize, len: usize) -> String {
    let copy = string.clone();
    return copy.chars().skip(start).take(len).collect();
}

fn path_to_service(path: &String) -> &'static str {

    if path.contains('@') {
        if path.contains(".tgz") {
            return "scoped-tarball";
        }
        return "scoped-json";
    }

    if path.contains("doc.json") {
        return "static-json";
    }

    if path.contains("doc.min.json") {
        return "corgi-json";
    }

    let len = path.len();

    if substr(&path, len - 4, 5) == ".tgz" {
        return "tarball";
    }

    if path.contains("/-/") {
        return "tie-fighter";
    }

    if !split_at(&path, ' ').contains('/') {
        return "json";
    }
    return "misc";
}

fn split_at(string: &String, find: char) -> String {
    return substr(&string, string.find(find).unwrap_or(0), string.len());
}
<commit_msg>adding minute<commit_after>use std::io;
use std::io::prelude::*;
//use std::fs::File;

extern crate time;

fn main() {

    // <134>2017-04-24T20:38:26Z cache-iad2645 fastly-logs-5-east[367774]: 54.87.185.35 "-" "GET
    // /npm/public/registry/i/is-utf8/_attachments/doc.json" 200 "npm/4.2.0 node/v7.8.0 linux x64"
    // "install" "26f82920d62b24e9" "HIT" "(null)" "cache-iad2645-IAD" "0" "1016" "1466" "251"

    // only quoted values have spaces.
    let mut value = String::new();
    let mut values = Vec::new();
    let mut state = "out";

    for byte in io::stdin().bytes() {
        let c = byte.unwrap() as char;

        if c == '\n' {
            // if i have a pending value add it to values
            if value.len() > 0 {
                values.push(value)
            }

            format(&values);

            value = String::new();
            values = Vec::new();
            continue;
        }

        if state == "out" {
            if c == '"' {
                state = "quoted";
                // skip quote
                continue;
            } else if c != ' ' {
                state = "value";
            }
        }

        if state == "value" {
            if c == ' ' {
                values.push(value);
                value = String::new();
                state = "out";
            } else if c != '"' {
                value.push(c);
            }
        }

        if state == "quoted" {
            if c == '"' {
                values.push(value);
                value = String::new();
                state = "out";
            } else {
                value.push(c);
            }
        }
    }
}

fn format(values: &Vec<String>) {
    let len = values.len();

    if len == 0 {
        return;
    }

    let date = parse_date(values[0].clone());
    let unixtime = logtime_to_unixtime(&date);
    let minute = unixtime - (unixtime % 60);

    // invalid time. wont count.
    if unixtime == 0 {
        return;
    }

    let pop = substr(&values[1], 6, 3);

    // if i have any values emit them.

    let service = path_to_service(&values[5]);

    let status = &values[6];

    let mut offset = 0;
    if len == 18 {
        offset = 1;
    }

    let egress_bytes = &values[15 + offset];
    let ingress_bytes = &values[16 + offset];

    for v in values {
        print!("{}, ", v);
    }

    println!("[{}]", len);

    println!("format {} {} {} {} {} {} {} {}",
             date,
             minute,
             unixtime,
             pop,
             service,
             status,
             egress_bytes,
             ingress_bytes);
}

fn parse_date(dateref: String) -> String {
    let mut date = dateref.clone();
    let offset_option = date.find('>');

    if offset_option != None {

        let offset = offset_option.unwrap() + 1;
        // -1 to remove the trailing Z
        let end = date.len() - offset - 1;

        //date = date.chars().skip(offset).take(end).collect();
        date = substr(&date, offset, end)
    }
    return date;
}

fn logtime_to_unixtime(date: &String) -> i64 {

    // 2017-04-24T20:38:26
    let unixtime = match time::strptime(date, "%Y-%m-%dT%H:%M:%S") {
        Ok(v) => v.to_timespec().sec,
        _ => 0,
    };

    return unixtime;
}

fn substr(string: &String, start: usize, len: usize) -> String {
    let copy = string.clone();
    return copy.chars().skip(start).take(len).collect();
}

fn path_to_service(path: &String) -> &'static str {

    if path.contains('@') {
        if path.contains(".tgz") {
            return "scoped-tarball";
        }
        return "scoped-json";
    }

    if path.contains("doc.json") {
        return "static-json";
    }

    if path.contains("doc.min.json") {
        return "corgi-json";
    }

    let len = path.len();

    if substr(&path, len - 4, 5) == ".tgz" {
        return "tarball";
    }

    if path.contains("/-/") {
        return "tie-fighter";
    }

    if !split_at(&path, ' ').contains('/') {
        return "json";
    }
    return "misc";
}

fn split_at(string: &String, find: char) -> String {
    return substr(&string, string.find(find).unwrap_or(0), string.len());
}
<|endoftext|>