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

use byteorder::{LittleEndian, ReadBytesExt};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::mem;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId};
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{ColorAttachmentType, GLContext};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            // Preprocess messages to guard against message floods.
            let mut msgs = vec![];
            let mut index_of_last_set_root_stacking_context_msg = None;
            let mut msg = match self.api_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break,
            };
            loop {
                match msg {
                    ApiMsg::SetRootStackingContext(..) => {
                        index_of_last_set_root_stacking_context_msg = Some(msgs.len())
                    }
                    _ => {}
                }
                msgs.push(msg);
                msg = match self.api_rx.try_recv() {
                    Err(_) => break,
                    Ok(msg) => msg,
                }
            }

            for (i, msg) in mem::replace(&mut msgs, vec![]).into_iter().enumerate() {
                match msg {
                    ApiMsg::SetRootStackingContext(..) if Some(i) !=
                            index_of_last_set_root_stacking_context_msg => {
                        // We're getting flooded with new frames. Ignore all but the last one.
                    }
                    _ => msgs.push(msg),
                }
            }

            // Process the messages that survived.
            for msg in msgs {
                match msg {
                    ApiMsg::AddRawFont(id, bytes) => {
                        profile_counters.font_templates.inc(bytes.len());
                        self.resource_cache
                            .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                    }
                    ApiMsg::AddNativeFont(id, native_font_handle) => {
                        self.resource_cache
                            .add_font_template(id, FontTemplate::Native(native_font_handle));
                    }
                    ApiMsg::AddImage(id, width, height, format, bytes) => {
                        profile_counters.image_templates.inc(bytes.len());
                        self.resource_cache.add_image_template(id,
                                                               width,
                                                               height,
                                                               format,
                                                               bytes);
                    }
                    ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                        self.resource_cache.update_image_template(id,
                                                                  width,
                                                                  height,
                                                                  format,
                                                                  bytes);
                    }
                    ApiMsg::DeleteImage(id) => {
                        self.resource_cache.delete_image_template(id);
                    }
                    ApiMsg::CloneApi(sender) => {
                        let result = self.next_namespace_id;

                        let IdNamespace(id_namespace) = self.next_namespace_id;
                        self.next_namespace_id = IdNamespace(id_namespace + 1);

                        sender.send(result).unwrap();
                    }
                    ApiMsg::SetRootStackingContext(stacking_context_id,
                                                   background_color,
                                                   epoch,
                                                   pipeline_id,
                                                   viewport_size,
                                                   stacking_contexts,
                                                   display_lists,
                                                   auxiliary_lists_descriptor) => {
                        for (id, stacking_context) in stacking_contexts.into_iter() {
                            self.scene.add_stacking_context(id,
                                                            pipeline_id,
                                                            epoch,
                                                            stacking_context);
                        }

                        let mut leftover_auxiliary_data = vec![];
                        let mut auxiliary_data;
                        loop {
                            auxiliary_data = self.payload_rx.recv().unwrap();
                            {
                                let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                let payload_stacking_context_id =
                                    payload_reader.read_u32::<LittleEndian>().unwrap();
                                let payload_epoch =
                                    payload_reader.read_u32::<LittleEndian>().unwrap();
                                if payload_epoch == epoch.0 &&
                                        payload_stacking_context_id ==
                                            stacking_context_id.0 {
                                    break
                                }
                            }
                            leftover_auxiliary_data.push(auxiliary_data)
                        }
                        for leftover_auxiliary_data in leftover_auxiliary_data {
                            self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                        }

                        let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                        for (display_list_id,
                             display_list_descriptor) in display_lists.into_iter() {
                            let mut built_display_list_data =
                                vec![0; display_list_descriptor.size()];
                            auxiliary_data.read_exact(&mut built_display_list_data[..])
                                          .unwrap();
                            let built_display_list =
                                BuiltDisplayList::from_data(built_display_list_data,
                                                            display_list_descriptor);
                            self.scene.add_display_list(display_list_id,
                                                        pipeline_id,
                                                        epoch,
                                                        built_display_list,
                                                        &mut self.resource_cache);
                        }

                        let mut auxiliary_lists_data =
                            vec![0; auxiliary_lists_descriptor.size()];
                        auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                        let auxiliary_lists =
                            AuxiliaryLists::from_data(auxiliary_lists_data,
                                                      auxiliary_lists_descriptor);
                        let frame = profile_counters.total_time.profile(|| {
                            self.scene.set_root_stacking_context(pipeline_id,
                                                                 epoch,
                                                                 stacking_context_id,
                                                                 background_color,
                                                                 viewport_size,
                                                                 &mut self.resource_cache,
                                                                 auxiliary_lists);

                            self.build_scene();
                            self.render()
                        });

                        self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                    }
                    ApiMsg::SetRootPipeline(pipeline_id) => {
                        let frame = profile_counters.total_time.profile(|| {
                            self.scene.set_root_pipeline_id(pipeline_id);

                            self.build_scene();
                            self.render()
                        });

                        self.publish_frame(frame, &mut profile_counters);
                    }
                    ApiMsg::Scroll(delta, cursor, move_phase) => {
                        let frame = profile_counters.total_time.profile(|| {
                            if self.frame.scroll(delta, cursor, move_phase) {
                                Some(self.render())
                            } else {
                                None
                            }
                        });

                        match frame {
                            Some(frame) => {
                                self.publish_frame(frame, &mut profile_counters);
                                self.notify_compositor_of_new_scroll_frame(true)
                            }
                            None => self.notify_compositor_of_new_scroll_frame(false),
                        }
                    }
                    ApiMsg::TickScrollingBounce => {
                        let frame = profile_counters.total_time.profile(|| {
                            self.frame.tick_scrolling_bounce_animations();
                            self.render()
                        });

                        self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                    }
                    ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                        // First, find the specific layer that contains the point.
                        let point = point / self.device_pixel_ratio;
                        if let (Some(_), Some(root_scroll_layer_id)) =
                                (self.scene.root_pipeline_id,
                                 self.frame.root_scroll_layer_id) {
                            if let Some(scroll_layer_id) =
                                    self.frame.get_scroll_layer(&point,
                                                                root_scroll_layer_id) {
                                if let Some(layer) = self.frame
                                                         .layers
                                                         .get(&scroll_layer_id) {
                                    // Now, because we send a *pipeline ID*, not a layer
                                    // ID, as a response, we need the translated point to
                                    // be relative to the origin of that pipeline in the
                                    // scene. So we need to find the root layer for the
                                    // pipeline. (We don't send layer IDs because layer IDs
                                    // are internal to WebRender; Servo won't know what to
                                    // do with them.)
                                    if let Some(layer_id) =
                                            self.frame.root_scroll_layer_for_pipeline(
                                                layer.pipeline_id) {
                                        if let Some(layer) = self.frame
                                                                 .layers
                                                                     .get(&layer_id) {
                                            let point = point - layer.world_origin -
                                                layer.scrolling.offset;
                                            tx.send((point, layer.pipeline_id)).unwrap();
                                            continue
                                        }
                                    }
                                }
                            }
                        }
                        tx.send((point, PipelineId(0, 0))).unwrap()
                    }
                    ApiMsg::GetScrollLayerState(tx) => {
                        tx.send(self.frame.get_scroll_layer_state())
                          .unwrap()
                    }
                    ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                        if let Some(ref handle) = self.webrender_context_handle {
                            match GLContext::<NativeGLContext>::new(
                                    size,
                                    attributes,
                                    ColorAttachmentType::Texture,
                                    Some(handle)) {
                                Ok(ctx) => {
                                    let id = WebGLContextId(new_id());

                                    let (real_size, texture_id) = {
                                        let draw_buffer = ctx.borrow_draw_buffer()
                                                             .unwrap();
                                        (draw_buffer.size(),
                                         draw_buffer.get_bound_texture_id().unwrap())
                                    };

                                    let limits = ctx.borrow_limits().clone();

                                    self.webgl_contexts.insert(id, ctx);

                                    self.resource_cache
                                        .add_webgl_texture(id,
                                                           TextureId(texture_id),
                                                           real_size);

                                    tx.send(Ok((id, limits))).unwrap();
                                },
                                Err(msg) => {
                                    tx.send(Err(msg.to_owned())).unwrap();
                                }
                            }
                        } else {
                            tx.send(Err("Not implemented yet".to_owned())).unwrap();
                        }
                    }
                    ApiMsg::WebGLCommand(context_id, command) => {
                        // TODO: Buffer the commands and only apply them here if they need
                        // to be synchronous.
                        let ctx = self.webgl_contexts.get(&context_id).unwrap();
                        ctx.make_current().unwrap();
                        command.apply(ctx);
                        self.current_bound_webgl_context_id = Some(context_id);
                    }
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let pending_updates = self.frame.pending_updates();
        let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();
    }

    fn publish_frame_and_notify_compositor(&mut self,
                                           frame: RendererFrame,
                                           profile_counters: &mut BackendProfileCounters) {
        self.publish_frame(frame, profile_counters);

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }

    fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed);
    }
}

<commit_msg>Revert "Defend against getting flooded with new frames"<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/. */

use byteorder::{LittleEndian, ReadBytesExt};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId};
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{ColorAttachmentType, GLContext};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();
            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::DeleteImage(id) => {
                            self.resource_cache.delete_image_template(id);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id,
                                                       viewport_size,
                                                       stacking_contexts,
                                                       display_lists,
                                                       auxiliary_lists_descriptor) => {
                            for (id, stacking_context) in stacking_contexts.into_iter() {
                                self.scene.add_stacking_context(id,
                                                                pipeline_id,
                                                                epoch,
                                                                stacking_context);
                            }

                            let mut leftover_auxiliary_data = vec![];
                            let mut auxiliary_data;
                            loop {
                                auxiliary_data = self.payload_rx.recv().unwrap();
                                {
                                    let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                    let payload_stacking_context_id =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    let payload_epoch =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    if payload_epoch == epoch.0 &&
                                            payload_stacking_context_id == stacking_context_id.0 {
                                        break
                                    }
                                }
                                leftover_auxiliary_data.push(auxiliary_data)
                            }
                            for leftover_auxiliary_data in leftover_auxiliary_data {
                                self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                            }

                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                            for (display_list_id,
                                 display_list_descriptor) in display_lists.into_iter() {
                                let mut built_display_list_data =
                                    vec![0; display_list_descriptor.size()];
                                auxiliary_data.read_exact(&mut built_display_list_data[..])
                                              .unwrap();
                                let built_display_list =
                                    BuiltDisplayList::from_data(built_display_list_data,
                                                                display_list_descriptor);
                                self.scene.add_display_list(display_list_id,
                                                            pipeline_id,
                                                            epoch,
                                                            built_display_list,
                                                            &mut self.resource_cache);
                            }

                            let mut auxiliary_lists_data =
                                vec![0; auxiliary_lists_descriptor.size()];
                            auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                            let auxiliary_lists =
                                AuxiliaryLists::from_data(auxiliary_lists_data,
                                                          auxiliary_lists_descriptor);
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     viewport_size,
                                                                     &mut self.resource_cache,
                                                                     auxiliary_lists);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta, cursor, move_phase) => {
                            let frame = profile_counters.total_time.profile(|| {
                                if self.frame.scroll(delta, cursor, move_phase) {
                                    Some(self.render())
                                } else {
                                    None
                                }
                            });

                            match frame {
                                Some(frame) => {
                                    self.publish_frame(frame, &mut profile_counters);
                                    self.notify_compositor_of_new_scroll_frame(true)
                                }
                                None => self.notify_compositor_of_new_scroll_frame(false),
                            }
                        }
                        ApiMsg::TickScrollingBounce => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.frame.tick_scrolling_bounce_animations();
                                self.render()
                            });

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // First, find the specific layer that contains the point.
                            let point = point / self.device_pixel_ratio;
                            if let (Some(root_pipeline_id), Some(root_scroll_layer_id)) =
                                    (self.scene.root_pipeline_id,
                                     self.frame.root_scroll_layer_id) {
                                if let Some(scroll_layer_id) =
                                        self.frame.get_scroll_layer(&point, root_scroll_layer_id) {
                                    if let Some(layer) = self.frame.layers.get(&scroll_layer_id) {
                                        // Now, because we send a *pipeline ID*, not a layer ID, as
                                        // a response, we need the translated point to be relative
                                        // to the origin of that pipeline in the scene. So we need
                                        // to find the root layer for the pipeline. (We don't send
                                        // layer IDs because layer IDs are internal to WebRender;
                                        // Servo won't know what to do with them.)
                                        if let Some(layer_id) =
                                                self.frame.root_scroll_layer_for_pipeline(
                                                    layer.pipeline_id) {
                                            if let Some(layer) = self.frame.layers.get(&layer_id) {
                                                let point = point - layer.world_origin -
                                                    layer.scrolling.offset;
                                                tx.send((point, layer.pipeline_id)).unwrap();
                                                continue
                                            }
                                        }
                                    }
                                }
                            }
                            tx.send((point, PipelineId(0, 0))).unwrap()
                        }
                        ApiMsg::GetScrollLayerState(tx) => {
                            tx.send(self.frame.get_scroll_layer_state())
                              .unwrap()
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        let limits = ctx.borrow_limits().clone();

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok((id, limits))).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            // TODO: Buffer the commands and only apply them here if they need to
                            // be synchronous.
                            let ctx = self.webgl_contexts.get(&context_id).unwrap();
                            ctx.make_current().unwrap();
                            command.apply(ctx);
                            self.current_bound_webgl_context_id = Some(context_id);
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let pending_updates = self.frame.pending_updates();
        let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();
    }

    fn publish_frame_and_notify_compositor(&mut self,
                                           frame: RendererFrame,
                                           profile_counters: &mut BackendProfileCounters) {
        self.publish_frame(frame, profile_counters);

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }

    fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed);
    }
}

<|endoftext|>


<commit_before>#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate image;

use std::io::Cursor;
use image::ImageResult;
use image::codecs::openexr::*;
use std::io::Seek;
use std::io::BufRead;
use std::convert::TryFrom;
use image::ImageDecoder;
use image::ImageEncoder;
use image::ColorType;

// "just dont panic"
fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
    use std::io::Write;

    /// Read the file from the specified path into an `Rgba32FImage`.
    // TODO this method should probably already exist in the main image crate
    fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(u32,u32,Vec<u8>)> {
        let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
        let (width, height) = decoder.dimensions();

        let mut buffer = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
        decoder.read_image(buffer.as_mut_slice())?;

        Ok((width, height, buffer))
    }

    /// Write an `Rgba32FImage`.
    /// Assumes the writer is buffered. In most cases,
    /// you should wrap your writer in a `BufWriter` for best performance.
    // TODO this method should probably already exist in the main image crate
    fn write_rgba_image(write: impl Write/* + Seek*/, (width, height, data): &(u32,u32,Vec<u8>)) -> ImageResult<()> {
        OpenExrEncoder::new(write).write_image(
            data.as_slice(), *width, *height,
            ColorType::Rgba32F
        )
    }

    let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    let mut bytes = Vec::with_capacity(bytes.len() + 20);
    write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?;

    let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    // if both images are valid, assert read/write consistency
    assert_eq!(decoded_image, redecoded_image, "image was valid but was not reproducible");
    Ok(())
}

fuzz_target!(|data: &[u8]| {
    let _img = roundtrip(data); // fixme not optimized away?
});
<commit_msg>fix fuzz script for exr<commit_after>#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate image;

use std::io::Cursor;
use image::ImageResult;
use image::codecs::openexr::*;
use std::io::Seek;
use std::io::BufRead;
use std::convert::TryFrom;
use image::ImageDecoder;
use image::ImageEncoder;
use image::ColorType;

// "just dont panic"
fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
    use std::io::Write;

    /// Read the file from the specified path into an `Rgba32FImage`.
    // TODO this method should probably already exist in the main image crate
    fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(u32,u32,Vec<u8>)> {
        let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
        let (width, height) = decoder.dimensions();

        let mut buffer = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
        decoder.read_image(buffer.as_mut_slice())?;

        Ok((width, height, buffer))
    }

    /// Write an `Rgba32FImage`.
    /// Assumes the writer is buffered. In most cases,
    /// you should wrap your writer in a `BufWriter` for best performance.
    // TODO this method should probably already exist in the main image crate
    fn write_rgba_image(write: impl Write + Seek, (width, height, data): &(u32,u32,Vec<u8>)) -> ImageResult<()> {
        OpenExrEncoder::new(write).write_image(
            data.as_slice(), *width, *height,
            ColorType::Rgba32F
        )
    }

    let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    let mut bytes = Vec::with_capacity(bytes.len() + 20);
    write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?;

    let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    // if both images are valid, assert read/write consistency
    assert_eq!(decoded_image, redecoded_image, "image was valid but was not reproducible");
    Ok(())
}

fuzz_target!(|data: &[u8]| {
    let _img = roundtrip(data); // fixme not optimized away?
});
<|endoftext|>


<commit_before>#[cfg(not(target_os = "macos"))]
static LIBFUSE_NAME: &str = "fuse";

#[cfg(target_os = "macos")]
static LIBFUSE_NAME: &str = "osxfuse";

fn main () {
    pkg_config::Config::new().atleast_version("2.6.0").probe(LIBFUSE_NAME).unwrap();
}
<commit_msg>Clean up fuse-sys build.rs and output errors on stderr<commit_after>#[cfg(not(target_os = "macos"))]
const LIBFUSE_NAME: &str = "fuse";

#[cfg(target_os = "macos")]
const LIBFUSE_NAME: &str = "osxfuse";

fn main() {
    pkg_config::Config::new()
        .atleast_version("2.6.0")
        .probe(LIBFUSE_NAME)
        .map_err(|e| eprintln!("{}", e))
        .unwrap();
}
<|endoftext|>


<commit_before>use state::{Named, Editable, Undoable, Selectable};
use termion::event::{Event, Key, MouseEvent};
use view::View;
use std::cmp;
use clipboard::ClipboardContext;
use super::{State, treat_insert_event};

pub fn treat_selected_event<T>(content: &mut T, view: &mut View, event: Event, state: &mut State)
    where T: Selectable + Editable + Named + Undoable
{
    match event {
        Event::Key(Key::Ctrl('c')) => {
            let (beg, end) = content.sel().unwrap();

            let selection: String = content.iter().skip(beg).take(end - beg + 1).collect();
            let mut ctx = ClipboardContext::new().unwrap();
            ctx.set_contents(selection).unwrap();

            content.reset_sel();
            *state = State::Insert;
        }
        Event::Key(Key::Ctrl('x')) => {
            let (beg, end) = content.sel().unwrap();

            let selection: String = content.iter().skip(beg).take(end - beg + 1).collect();
            let mut ctx = ClipboardContext::new().unwrap();
            ctx.set_contents(selection).unwrap();

            delete_sel(content);
            view.adjust_view(content.line());

            content.reset_sel();
            *state = State::Insert;
        }
        Event::Key(Key::Backspace) => {
            delete_sel(content);
            view.adjust_view(content.line());
            content.reset_sel();
            *state = State::Insert;
        }
        _ => {
            content.reset_sel();
            *state = State::Insert;

            treat_insert_event(content, view, event, state)
        }
    }
}

fn delete_sel<T>(content: &mut T)
    where T: Selectable + Editable
{
    let (beg, end) = content.sel().unwrap();
    let end = cmp::min(end + 1, content.len() - 1);
    content.move_to(end);
    for _ in beg..end {
        content.delete();
    }
}

pub fn treat_select_event<T>(content: &mut T, view: &mut View, event: Event, state: &mut State)
    where T: Editable + Selectable
{
    match event {
        Event::Mouse(MouseEvent::Hold(x, y)) => {
            let (line, col) = view.translate_coordinates(content, x, y);
            content.move_at(line, col);
            if let State::Select(origin) = *state {
                let sel = (cmp::min(origin, content.pos() - 1),
                           cmp::max(origin, content.pos() - 1));
                content.set_sel(sel);
            } else {
                panic!("Treating select event when event is not a Select");
            }
        }
        Event::Mouse(MouseEvent::Release(x, y)) => {
            let (line, col) = view.translate_coordinates(content, x, y);
            content.move_at(line, col);
            if let State::Select(origin) = *state {
                if origin != content.pos() {
                    let sel = (cmp::min(origin, content.pos() - 1),
                               cmp::max(origin, content.pos() - 1));
                    content.set_sel(sel);
                    *state = State::Selected;
                } else {
                    *state = State::Insert;
                }
            } else {
                panic!("Treating select event when event is not a Select");
            }

        }
        _ => {}
    }
}
<commit_msg>fixed selection bug<commit_after>use state::{Named, Editable, Undoable, Selectable};
use termion::event::{Event, Key, MouseEvent};
use view::View;
use std::cmp;
use clipboard::ClipboardContext;
use super::{State, treat_insert_event};

pub fn treat_selected_event<T>(content: &mut T, view: &mut View, event: Event, state: &mut State)
    where T: Selectable + Editable + Named + Undoable
{
    match event {
        Event::Key(Key::Ctrl('c')) => {
            let (beg, end) = content.sel().unwrap();

            let selection: String = content.iter().skip(beg).take(end - beg + 1).collect();
            let mut ctx = ClipboardContext::new().unwrap();
            ctx.set_contents(selection).unwrap();

            content.reset_sel();
            *state = State::Insert;
        }
        Event::Key(Key::Ctrl('x')) => {
            let (beg, end) = content.sel().unwrap();

            let selection: String = content.iter().skip(beg).take(end - beg + 1).collect();
            let mut ctx = ClipboardContext::new().unwrap();
            ctx.set_contents(selection).unwrap();

            delete_sel(content);
            view.adjust_view(content.line());

            content.reset_sel();
            *state = State::Insert;
        }
        Event::Key(Key::Backspace) => {
            delete_sel(content);
            view.adjust_view(content.line());
            content.reset_sel();
            *state = State::Insert;
        }
        _ => {
            content.reset_sel();
            *state = State::Insert;

            treat_insert_event(content, view, event, state)
        }
    }
}

fn delete_sel<T>(content: &mut T)
    where T: Selectable + Editable
{
    let (beg, end) = content.sel().unwrap();
    let end = cmp::min(end + 1, content.len() - 1);
    content.move_to(end);
    for _ in beg..end {
        content.delete();
    }
}

pub fn treat_select_event<T>(content: &mut T, view: &mut View, event: Event, state: &mut State)
    where T: Editable + Selectable
{
    match event {
        Event::Mouse(MouseEvent::Hold(x, y)) => {
            let (line, col) = view.translate_coordinates(content, x, y);
            content.move_at(line, col);
            if let State::Select(origin) = *state {
                let sel = (cmp::min(origin, content.pos()), cmp::max(origin, content.pos()));
                content.set_sel(sel);
            } else {
                panic!("Treating select event when event is not a Select");
            }
        }
        Event::Mouse(MouseEvent::Release(x, y)) => {
            let (line, col) = view.translate_coordinates(content, x, y);
            content.move_at(line, col);
            if let State::Select(origin) = *state {
                if origin != content.pos() {
                    let sel = (cmp::min(origin, content.pos() - 1),
                               cmp::max(origin, content.pos() - 1));
                    content.set_sel(sel);
                    *state = State::Selected;
                } else {
                    *state = State::Insert;
                }
            } else {
                panic!("Treating select event when event is not a Select");
            }

        }
        _ => {}
    }
}
<|endoftext|>


<commit_before>
fn calculate_return(cost :f64, given :f64) -> f64 {
    return given - cost
}

#[test]
fn calculate_return_test(){
    assert!(calculate_return(13.23f64, 15.00f64) == 1.77f64, "expected {:?} is {:?}", 1.77f64, calculate_return(13.23f64, 15.00f64));
}

#[test]
fn calculate_return_test2(){
    assert!(calculate_return(132.23f64, 150.00f64) == 17.77f64, "expected {:?} is {:?}", 17.77f64, calculate_return(132.23f64, 150.00f64));
}

fn main() {
    println!("{}",calculate_return(123.45, 130.00));
}
<commit_msg>Delete cost_return.rs<commit_after><|endoftext|>


<commit_before>pub mod parse_graph;
pub mod parse_io;

use std;

extern crate rustc_serialize;
extern crate libc;

use {ContextPtr, JobPtr};
use JsonResponse;
use flow;
use libc::c_void;

use parsing::rustc_serialize::hex::FromHex;
use std::collections::HashMap;

use std::ptr;
extern crate imageflow_types as s;
extern crate serde;
extern crate serde_json;

use ::Context;

use ffi;

pub use self::parse_graph::GraphTranslator;
pub use self::parse_io::IoTranslator;
<commit_msg>refactor(core): delete dead code<commit_after>pub mod parse_graph;
pub mod parse_io;
pub use self::parse_graph::GraphTranslator;
pub use self::parse_io::IoTranslator;
<|endoftext|>


<commit_before>pub fn decode(ch:char) -> Option<u8> {
  match ch {
    '0' => Some(0),
    '1' => Some(1),
    '2' => Some(2),
    '3' => Some(3),
    '4' => Some(4),
    '5' => Some(5),
    '6' => Some(6),
    '7' => Some(7),
    '8' => Some(8),
    '9' => Some(9),

    'b' => Some(10),
    'c' => Some(11),
    'd' => Some(12),
    'e' => Some(13),
    'f' => Some(14),
    'g' => Some(15),
    'h' => Some(16),
    'j' => Some(17),
    'k' => Some(18),
    'm' => Some(19),
    'n' => Some(20),
    'p' => Some(21),
    'q' => Some(22),
    'r' => Some(23),
    's' => Some(24),
    't' => Some(25),
    'u' => Some(26),
    'v' => Some(27),
    'w' => Some(28),
    'x' => Some(29),
    'y' => Some(30),
    'z' => Some(31),
    _   => None
  }
}

pub fn encode(in:u8) -> Option<char> {
  match in {
    0   => Some('0'),
    1   => Some('1'),
    2   => Some('2'),
    3   => Some('3'),
    4   => Some('4'),
    5   => Some('5'),
    6   => Some('6'),
    7   => Some('7'),
    8   => Some('8'),
    9   => Some('9'),
    10  => Some('b'),
    11  => Some('c'),
    12  => Some('d'),
    13  => Some('e'),
    14  => Some('f'),
    15  => Some('g'),
    16  => Some('h'),
    17  => Some('j'),
    18  => Some('k'),
    19  => Some('m'),
    20  => Some('n'),
    21  => Some('p'),
    22  => Some('q'),
    23  => Some('r'),
    24  => Some('s'),
    25  => Some('t'),
    26  => Some('u'),
    27  => Some('v'),
    28  => Some('w'),
    29  => Some('x'),
    30  => Some('y'),
    31  => Some('z'),
    _   => None
  }
}

<commit_msg>Fixes geohash::base32::encode by not using a keyword for an identifier<commit_after>pub fn decode(ch:char) -> Option<u8> {
  match ch {
    '0' => Some(0),
    '1' => Some(1),
    '2' => Some(2),
    '3' => Some(3),
    '4' => Some(4),
    '5' => Some(5),
    '6' => Some(6),
    '7' => Some(7),
    '8' => Some(8),
    '9' => Some(9),

    'b' => Some(10),
    'c' => Some(11),
    'd' => Some(12),
    'e' => Some(13),
    'f' => Some(14),
    'g' => Some(15),
    'h' => Some(16),
    'j' => Some(17),
    'k' => Some(18),
    'm' => Some(19),
    'n' => Some(20),
    'p' => Some(21),
    'q' => Some(22),
    'r' => Some(23),
    's' => Some(24),
    't' => Some(25),
    'u' => Some(26),
    'v' => Some(27),
    'w' => Some(28),
    'x' => Some(29),
    'y' => Some(30),
    'z' => Some(31),
    _   => None
  }
}

pub fn encode(val:u8) -> Option<char> {
  match val {
    0   => Some('0'),
    1   => Some('1'),
    2   => Some('2'),
    3   => Some('3'),
    4   => Some('4'),
    5   => Some('5'),
    6   => Some('6'),
    7   => Some('7'),
    8   => Some('8'),
    9   => Some('9'),
    10  => Some('b'),
    11  => Some('c'),
    12  => Some('d'),
    13  => Some('e'),
    14  => Some('f'),
    15  => Some('g'),
    16  => Some('h'),
    17  => Some('j'),
    18  => Some('k'),
    19  => Some('m'),
    20  => Some('n'),
    21  => Some('p'),
    22  => Some('q'),
    23  => Some('r'),
    24  => Some('s'),
    25  => Some('t'),
    26  => Some('u'),
    27  => Some('v'),
    28  => Some('w'),
    29  => Some('x'),
    30  => Some('y'),
    31  => Some('z'),
    _   => None
  }
}

<|endoftext|>


<commit_before>#![feature(macro_rules)]
#![feature(globs)]

extern crate router = "route_recognizer";
extern crate conduit;

use std::collections::HashMap;
use std::any::{Any, AnyRefExt};

use router::{Router, Match};
use conduit::{Method, Handler, Request, Response};

pub struct RouteBuilder<E> {
    routers: HashMap<Method, Router<Box<Handler<E>>>>
}

macro_rules! method_map(
    ($method:ident => $variant:ty) => (
        pub fn $method<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                                -> &'a mut RouteBuilder<E>
        {
            self.map(conduit::$variant, pattern, handler)
        }
    )
)

impl<E> RouteBuilder<E> {
    pub fn new() -> RouteBuilder<E> {
        RouteBuilder { routers: HashMap::new() }
    }

    pub fn recognize<'a>(&'a self, method: &Method, path: &str) -> Result<Match<&'a Box<Handler<E>>>, String> {
        match self.routers.find(method) {
            None => Err(format!("No router found for {}", method)),
            Some(router) => router.recognize(path)
        }
    }

    pub fn map<'a, H: 'static + Handler<E>>(&'a mut self,
                                        method: Method, pattern: &str, handler: H)
                                        -> &'a mut RouteBuilder<E>
    {
        {
            let router = self.routers.find_or_insert_with(method, |_| Router::new());
            router.add(pattern, box handler as Box<Handler<E>>);
        }
        self
    }

    pub fn get<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder<E>
    {
        self.map(conduit::Get, pattern, handler)
    }

    pub fn post<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder<E>
    {
        self.map(conduit::Post, pattern, handler)
    }

    pub fn put<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder<E>
    {
        self.map(conduit::Put, pattern, handler)
    }

    pub fn delete<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder<E>
    {
        self.map(conduit::Delete, pattern, handler)
    }

    pub fn head<'a, H: 'static + Handler<E>>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder<E>
    {
        self.map(conduit::Head, pattern, handler)
    }
}

impl<E> conduit::Handler<E> for RouteBuilder<E> {
    fn call(&self, request: &mut Request) -> Result<Response, E> {
        let m = {
            let method = request.method();
            let path = request.path();

            self.recognize(&method, path).unwrap()
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert("router.params", box m.params.clone() as Box<Any>);
        }

        (*m.handler).call(request)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params<'a>(req: &'a mut Request) -> &'a router::Params {
    req.extensions().find(&"router.params")
        .and_then(|a| a.as_ref::<router::Params>())
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a mut Request {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

//impl<T: Request> RequestParams for T {}

#[cfg(test)]
mod tests {
    extern crate semver;
    use std::io::net::ip::IpAddr;
    use std::collections::HashMap;
    use std::io::MemReader;
    use super::*;

    use conduit;
    use conduit::{Handler, Method, Scheme, Host, Headers, Extensions};

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn as_conduit_handler() {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", handler1);
        router.get("/posts/:id", handler1);

        let mut req = RequestSentinel::new(conduit::Get, "/posts/1");
        let mut res = router.call(&mut req).unwrap();

        assert_eq!(res.status, (200, "OK"));
        assert_eq!(res.body.read_to_str().unwrap(), "1, Get".to_str());

        let mut req = RequestSentinel::new(conduit::Post, "/posts/10");
        let mut res = router.call(&mut req).unwrap();

        assert_eq!(res.status, (200, "OK"));
        assert_eq!(res.body.read_to_str().unwrap(), "10, Post".to_str());
    }

    fn handler1(req: &mut conduit::Request) -> Result<conduit::Response, ()> {
        let mut res = vec!();
        res.push(req.params()["id"]);
        res.push(format!("{}", req.method()));

        Ok(conduit::Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(res.connect(", ").into_bytes())
        })
    }
}
<commit_msg>Update to upstream conduit<commit_after>#![feature(macro_rules)]
#![feature(globs)]

extern crate router = "route_recognizer";
extern crate conduit;

use std::collections::HashMap;
use std::any::{Any, AnyRefExt};
use std::fmt::Show;

use router::{Router, Match};
use conduit::{Method, Handler, Request, Response};

pub struct RouteBuilder {
    routers: HashMap<Method, Router<Box<Handler>>>
}

macro_rules! method_map(
    ($method:ident => $variant:ty) => (
        pub fn $method<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                                -> &'a mut RouteBuilder
        {
            self.map(conduit::$variant, pattern, handler)
        }
    )
)

impl RouteBuilder {
    pub fn new() -> RouteBuilder {
        RouteBuilder { routers: HashMap::new() }
    }

    pub fn recognize<'a>(&'a self, method: &Method, path: &str) -> Result<Match<&'a Box<Handler>>, String> {
        match self.routers.find(method) {
            None => Err(format!("No router found for {}", method)),
            Some(router) => router.recognize(path)
        }
    }

    pub fn map<'a, H: 'static + Handler>(&'a mut self,
                                        method: Method, pattern: &str, handler: H)
                                        -> &'a mut RouteBuilder
    {
        {
            let router = self.routers.find_or_insert_with(method, |_| Router::new());
            router.add(pattern, box handler as Box<Handler>);
        }
        self
    }

    pub fn get<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder
    {
        self.map(conduit::Get, pattern, handler)
    }

    pub fn post<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder
    {
        self.map(conduit::Post, pattern, handler)
    }

    pub fn put<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder
    {
        self.map(conduit::Put, pattern, handler)
    }

    pub fn delete<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder
    {
        self.map(conduit::Delete, pattern, handler)
    }

    pub fn head<'a, H: 'static + Handler>(&'a mut self, pattern: &str, handler: H)
                                            -> &'a mut RouteBuilder
    {
        self.map(conduit::Head, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    fn call(&self, request: &mut Request) -> Result<Response, Box<Show>> {
        let m = {
            let method = request.method();
            let path = request.path();

            self.recognize(&method, path).unwrap()
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert("router.params", box m.params.clone() as Box<Any>);
        }

        (*m.handler).call(request)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params<'a>(req: &'a mut Request) -> &'a router::Params {
    req.extensions().find(&"router.params")
        .and_then(|a| a.as_ref::<router::Params>())
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a mut Request {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

//impl<T: Request> RequestParams for T {}

#[cfg(test)]
mod tests {
    extern crate semver;
    use std::io::net::ip::IpAddr;
    use std::collections::HashMap;
    use std::io::MemReader;
    use super::*;

    use conduit;
    use conduit::{Handler, Method, Scheme, Host, Headers, Extensions};

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn as_conduit_handler() {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", handler1);
        router.get("/posts/:id", handler1);

        let mut req = RequestSentinel::new(conduit::Get, "/posts/1");
        let mut res = router.call(&mut req).ok().expect("No response");

        assert_eq!(res.status, (200, "OK"));
        assert_eq!(res.body.read_to_str().unwrap(), "1, Get".to_str());

        let mut req = RequestSentinel::new(conduit::Post, "/posts/10");
        let mut res = router.call(&mut req).ok().expect("No response");

        assert_eq!(res.status, (200, "OK"));
        assert_eq!(res.body.read_to_str().unwrap(), "10, Post".to_str());
    }

    fn handler1(req: &mut conduit::Request) -> Result<conduit::Response, ()> {
        let mut res = vec!();
        res.push(req.params()["id"]);
        res.push(format!("{}", req.method()));

        Ok(conduit::Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(res.connect(", ").into_bytes())
        })
    }
}
<|endoftext|>


<commit_before>use lru_cache::LruCache;

use cache::AssetLoadContext;

use std::collections::HashSet;

/// Currently assumes that values are taken at the lower left corner of each cell.
#[derive(Serialize, Deserialize)]
pub struct Raster<T: Into<f64> + Copy> {
    pub width: usize,
    pub height: usize,
    pub cell_size: f64,

    pub xllcorner: f64,
    pub yllcorner: f64,

    pub values: Vec<T>,
}

impl<T: Into<f64> + Copy> Raster<T> {
    pub fn interpolate(&self, latitude: f64, longitude: f64) -> Option<f64> {
        let x = (latitude - self.xllcorner) / self.cell_size;
        let y = (longitude - self.yllcorner) / self.cell_size;

        let y = (self.height - 1) as f64 - y;

        let fx = x.floor() as usize;
        let fy = y.floor() as usize;
        if x < 0.0 || fx >= self.width - 1 || y < 0.0 || fy >= self.height - 1 {
            return None;
        }

        let h00 = self.values[fx + fy * self.width].into();
        let h10 = self.values[fx + 1 + fy * self.width].into();
        let h01 = self.values[fx + (fy + 1) * self.width].into();
        let h11 = self.values[fx + 1 + (fy + 1) * self.width].into();
        let h0 = h00 + (h01 - h00) * (y - fy as f64);
        let h1 = h10 + (h11 - h10) * (y - fy as f64);
        Some(h0 + (h1 - h0) * (x - fx as f64))
    }
}

pub(crate) trait RasterSource {
    type Type: Into<f64> + Copy;
    fn load(
        &self,
        context: &mut AssetLoadContext,
        latitude: i16,
        longitude: i16,
    ) -> Option<Raster<Self::Type>>;
}

pub(crate) struct RasterCache<T: Into<f64> + Copy> {
    source: Box<RasterSource<Type = T>>,
    holes: HashSet<(i16, i16)>,
    rasters: LruCache<(i16, i16), Raster<T>>,
}
impl<T: Into<f64> + Copy> RasterCache<T> {
    pub fn new(source: Box<RasterSource<Type = T>>, size: usize) -> Self {
        Self {
            source,
            holes: HashSet::new(),
            rasters: LruCache::new(size),
        }
    }
    pub fn interpolate(
        &mut self,
        context: &mut AssetLoadContext,
        latitude: f64,
        longitude: f64,
    ) -> Option<f64> {
        let key = (latitude.floor() as i16, longitude.floor() as i16);

        if self.holes.contains(&key) {
            return None;
        }
        if let Some(dem) = self.rasters.get_mut(&key) {
            return dem.interpolate(latitude, longitude);
        }

        match self.source.load(context, key.0, key.1) {
            Some(raster) => {
                let value = raster.interpolate(latitude, longitude);
                // assert!(value.is_some());
                self.rasters.insert(key, raster);
                value
            }
            None => {
                self.holes.insert(key);
                None
            }
        }
    }
}

/// Currently assumes that values are taken at the *center* of cells.
pub(crate) struct GlobalRaster<T: Into<f64> + Copy> {
    pub width: usize,
    pub height: usize,
    pub bands: usize,
    pub values: Vec<T>,
}
impl<T: Into<f64> + Copy> GlobalRaster<T> {
    fn get(&self, x: i64, y: i64, band: usize) -> f64 {
        let y = y.max(0).min(self.height as i64) as usize;
        let x = (((x % self.width as i64) + self.width as i64) % self.width as i64) as usize;
        self.values[(x + y * self.width) * self.bands + band].into()
    }

    pub fn interpolate(&self, latitude: f64, longitude: f64, band: usize) -> f64 {
        assert!(latitude >= -90.0 && latitude <= 90.0);

        let x = (longitude + 180.0) / 360.0 * self.width as f64 - 0.5;
        let y = (90.0 - latitude) / 180.0 * self.height as f64 - 0.5;

        let fx = x.floor() as i64;
        let fy = y.floor() as i64;

        let h00 = self.get(fx, fy, band);
        let h10 = self.get(fx, 1 + fy, band);
        let h01 = self.get(fx, fy + 1, band);
        let h11 = self.get(fx + 1, fy + 1, band);
        let h0 = h00 + (h01 - h00) * (y - fy as f64);
        let h1 = h10 + (h11 - h10) * (y - fy as f64);
        h0 + (h1 - h0) * (x - fx as f64)
    }
}
<commit_msg>Fix bug in GlobalRaster interpolation<commit_after>use lru_cache::LruCache;

use cache::AssetLoadContext;

use std::collections::HashSet;

/// Currently assumes that values are taken at the lower left corner of each cell.
#[derive(Serialize, Deserialize)]
pub struct Raster<T: Into<f64> + Copy> {
    pub width: usize,
    pub height: usize,
    pub cell_size: f64,

    pub xllcorner: f64,
    pub yllcorner: f64,

    pub values: Vec<T>,
}

impl<T: Into<f64> + Copy> Raster<T> {
    pub fn interpolate(&self, latitude: f64, longitude: f64) -> Option<f64> {
        let x = (latitude - self.xllcorner) / self.cell_size;
        let y = (longitude - self.yllcorner) / self.cell_size;

        let y = (self.height - 1) as f64 - y;

        let fx = x.floor() as usize;
        let fy = y.floor() as usize;
        if x < 0.0 || fx >= self.width - 1 || y < 0.0 || fy >= self.height - 1 {
            return None;
        }

        let h00 = self.values[fx + fy * self.width].into();
        let h10 = self.values[fx + 1 + fy * self.width].into();
        let h01 = self.values[fx + (fy + 1) * self.width].into();
        let h11 = self.values[fx + 1 + (fy + 1) * self.width].into();
        let h0 = h00 + (h01 - h00) * (y - fy as f64);
        let h1 = h10 + (h11 - h10) * (y - fy as f64);
        Some(h0 + (h1 - h0) * (x - fx as f64))
    }
}

pub(crate) trait RasterSource {
    type Type: Into<f64> + Copy;
    fn load(
        &self,
        context: &mut AssetLoadContext,
        latitude: i16,
        longitude: i16,
    ) -> Option<Raster<Self::Type>>;
}

pub(crate) struct RasterCache<T: Into<f64> + Copy> {
    source: Box<RasterSource<Type = T>>,
    holes: HashSet<(i16, i16)>,
    rasters: LruCache<(i16, i16), Raster<T>>,
}
impl<T: Into<f64> + Copy> RasterCache<T> {
    pub fn new(source: Box<RasterSource<Type = T>>, size: usize) -> Self {
        Self {
            source,
            holes: HashSet::new(),
            rasters: LruCache::new(size),
        }
    }
    pub fn interpolate(
        &mut self,
        context: &mut AssetLoadContext,
        latitude: f64,
        longitude: f64,
    ) -> Option<f64> {
        let key = (latitude.floor() as i16, longitude.floor() as i16);

        if self.holes.contains(&key) {
            return None;
        }
        if let Some(dem) = self.rasters.get_mut(&key) {
            return dem.interpolate(latitude, longitude);
        }

        match self.source.load(context, key.0, key.1) {
            Some(raster) => {
                let value = raster.interpolate(latitude, longitude);
                // assert!(value.is_some());
                self.rasters.insert(key, raster);
                value
            }
            None => {
                self.holes.insert(key);
                None
            }
        }
    }
}

/// Currently assumes that values are taken at the *center* of cells.
pub(crate) struct GlobalRaster<T: Into<f64> + Copy> {
    pub width: usize,
    pub height: usize,
    pub bands: usize,
    pub values: Vec<T>,
}
impl<T: Into<f64> + Copy> GlobalRaster<T> {
    fn get(&self, x: i64, y: i64, band: usize) -> f64 {
        let y = y.max(0).min(self.height as i64) as usize;
        let x = (((x % self.width as i64) + self.width as i64) % self.width as i64) as usize;
        self.values[(x + y * self.width) * self.bands + band].into()
    }

    pub fn interpolate(&self, latitude: f64, longitude: f64, band: usize) -> f64 {
        assert!(latitude >= -90.0 && latitude <= 90.0);

        let x = (longitude + 180.0) / 360.0 * self.width as f64 - 0.5;
        let y = (90.0 - latitude) / 180.0 * self.height as f64 - 0.5;

        let fx = x.floor() as i64;
        let fy = y.floor() as i64;

        let h00 = self.get(fx, fy, band);
        let h10 = self.get(fx + 1, fy, band);
        let h01 = self.get(fx, fy + 1, band);
        let h11 = self.get(fx + 1, fy + 1, band);
        let h0 = h00 + (h01 - h00) * (y - fy as f64);
        let h1 = h10 + (h11 - h10) * (y - fy as f64);
        h0 + (h1 - h0) * (x - fx as f64)
    }
}
<|endoftext|>


<commit_before>use artano::Annotation;
use config::resource::Resource;
use error::Cause;
use std::borrow::Cow;
use std::error;
use std::fmt;
use std::path::{Path, PathBuf};
use std::result;

// How the hell do you make a path buffer from command line input if command line input is a
// string but a path buffer itself is technically not because it isn't validated UTF8?

type Result<T> = result::Result<T, BuildOptionsError>;

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<Annotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub scale_mult: f32,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Result<Self> {
        read_command()
    }
}

pub struct OptionsBuilder {
    base_image: Option<String>,
    annotations: Vec<Annotation>,
    output_path: Option<String>,
    output_format: OutputFormat,
    scale_mult: f32,
    font_path: Cow<'static, str>,
    debug: bool,
}

impl OptionsBuilder {
    fn new() -> OptionsBuilder {
        OptionsBuilder {
            base_image: None,
            annotations: Vec::new(),
            output_path: None,
            output_format: OutputFormat::Jpg,
            scale_mult: 1.0,
            font_path: default_font(),
            debug: false,
        }
    }
}

impl OptionsBuilder {
    fn build(self) -> Result<Options> {
        let input_path: PathBuf = self.base_image.as_ref().unwrap().into();
        if input_path.file_name().is_none() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::ImagePath,
                description: Cow::from(
                    "The provided image path does not appear to have a filename",
                ),
                cause: None,
            });
        }

        let output_format = self.output_format;
        let output_path = self.output_path.map(|s| s.into()).unwrap_or_else(|| {
            create_output_file_path(&input_path, output_format)
        });

        let annotations = if self.annotations.is_empty() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::Annotations,
                description: Cow::from("No annotations provided"),
                cause: None,
            });
        } else {
            self.annotations
        };

        Ok(Options {
            base_image: Resource::new(self.base_image.unwrap()),
            annotations,
            output_path,
            output_format,
            scale_mult: self.scale_mult,
            font_path: self.font_path.to_string().into(),
            debug: self.debug,
        })
    }
}

#[derive(Debug)]
pub struct BuildOptionsError {
    kind: BuildOptionsErrorKind,
    description: Cow<'static, str>,
    cause: Option<Cause>,
}

#[derive(Debug)]
enum BuildOptionsErrorKind {
    Annotations,
    ImagePath,
    ScalingMultiplier,
}

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

impl error::Error for BuildOptionsError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&error::Error> {
        self.cause.as_ref().map(|cause| cause.as_ref())
    }
}

fn read_command() -> Result<Options> {
    use clap::ArgGroup;

    let text_group = ArgGroup::with_name("text_group")
        .args(&["caption", "bottom", "top", "middle"])
        .multiple(true);

    let encoding_group = ArgGroup::with_name("enc_group").args(&["encoding", "jpg", "png"]);

    let app =
        clap_app!(annatar =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg image: +required "Path to an image to be annotated")
        (@arg caption: "A message to be added to the bottom of the image")
        (@arg bottom: -b --bottom +takes_value "A message to be added to the bottom of the image")
        (@arg top: -t --top +takes_value "A message to be added to the top of the image")
        (@arg middle: -m --middle +takes_value "A message to be added to the middle of the image")
        (@arg output: -o --output +takes_value
            "Sets an output path for the new image (default: <image path>/<image name>.ann.<ext>)")
        (@arg scale: -s --scale +takes_value "Sets the scale multiplier for annotations")
        (@arg font: -f --font +takes_value "Sets the path of the font to be used (default: Impact)")
        (@arg debug: -d --debug "Save edge detection ... thing to disk")
        (@arg encoding: -e --encoding +takes_value "Set JPG or PNG")
        (@arg jpg: --jpg "Set JPG mode (default)")
        (@arg png: --png "Set PNG mode")
    );

    // Much easier to set up argument groups outside macro.
    let app = app.group(text_group).group(encoding_group);

    let matches = app.get_matches();
    let mut options = OptionsBuilder::new();

    options.base_image = Some(matches.value_of("image").unwrap().to_string());
    options.output_path = matches.value_of("output").map(|s| s.to_string());

    if let Some(scale_multiplier) = matches.value_of("scale") {
        let multiplier = scale_multiplier.parse::<f32>().map_err(|e| {
            BuildOptionsError {
                kind: BuildOptionsErrorKind::ScalingMultiplier,
                description: Cow::from("Scaling multiplier must be a decimal value"),
                cause: Some(Box::new(e)),
            }
        })?;
        options.scale_mult = multiplier;
    }

    if let Some(font_path) = matches.value_of("font") {
        options.font_path = Cow::from(font_path.to_string());
    }

    if let Some(caption) = matches.value_of("caption") {
        options.annotations.push(Annotation::bottom(caption));
    }

    if let Some(caption) = matches.value_of("top") {
        options.annotations.push(Annotation::top(caption));
    }

    if let Some(caption) = matches.value_of("middle") {
        options.annotations.push(Annotation::middle(caption));
    }

    if let Some(caption) = matches.value_of("bottom") {
        options.annotations.push(Annotation::bottom(caption));
    }

    if matches.is_present("png") {
        options.output_format = OutputFormat::Png;
    } else if let Some(format) = matches.value_of("encoding") {
        options.output_format = match &*format {
            "png" | "PNG" => OutputFormat::Png,
            _ => OutputFormat::Jpg,
        };
    }

    options.debug = matches.is_present("debug");

    options.build()
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}

fn create_output_file_path(input_path: &Path, output_format: OutputFormat) -> PathBuf {
    // I unwrap this because clap already converted it to a string, implying it's valid utf-8.
    let mut file_name = input_path
        .file_name()
        .unwrap()
        .to_str()
        .unwrap()
        .to_string();
    if let Some(last_segment_idx) = file_name.rfind('.') {
        file_name.truncate(last_segment_idx);
    }

    match output_format {
        OutputFormat::Png => file_name.push_str("-annotated.png"),
        _ => file_name.push_str("-annotated.jpg"),
    }

    file_name.into()
}
<commit_msg>Default to PNG<commit_after>use artano::Annotation;
use config::resource::Resource;
use error::Cause;
use std::borrow::Cow;
use std::error;
use std::fmt;
use std::path::{Path, PathBuf};
use std::result;

// How the hell do you make a path buffer from command line input if command line input is a
// string but a path buffer itself is technically not because it isn't validated UTF8?

type Result<T> = result::Result<T, BuildOptionsError>;

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<Annotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub scale_mult: f32,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Result<Self> {
        read_command()
    }
}

pub struct OptionsBuilder {
    base_image: Option<String>,
    annotations: Vec<Annotation>,
    output_path: Option<String>,
    output_format: OutputFormat,
    scale_mult: f32,
    font_path: Cow<'static, str>,
    debug: bool,
}

impl OptionsBuilder {
    fn new() -> OptionsBuilder {
        OptionsBuilder {
            base_image: None,
            annotations: Vec::new(),
            output_path: None,
            output_format: OutputFormat::Jpg,
            scale_mult: 1.0,
            font_path: default_font(),
            debug: false,
        }
    }
}

impl OptionsBuilder {
    fn build(self) -> Result<Options> {
        let input_path: PathBuf = self.base_image.as_ref().unwrap().into();
        if input_path.file_name().is_none() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::ImagePath,
                description: Cow::from(
                    "The provided image path does not appear to have a filename",
                ),
                cause: None,
            });
        }

        let output_format = self.output_format;
        let output_path = self.output_path.map(|s| s.into()).unwrap_or_else(|| {
            create_output_file_path(&input_path, output_format)
        });

        let annotations = if self.annotations.is_empty() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::Annotations,
                description: Cow::from("No annotations provided"),
                cause: None,
            });
        } else {
            self.annotations
        };

        Ok(Options {
            base_image: Resource::new(self.base_image.unwrap()),
            annotations,
            output_path,
            output_format,
            scale_mult: self.scale_mult,
            font_path: self.font_path.to_string().into(),
            debug: self.debug,
        })
    }
}

#[derive(Debug)]
pub struct BuildOptionsError {
    kind: BuildOptionsErrorKind,
    description: Cow<'static, str>,
    cause: Option<Cause>,
}

#[derive(Debug)]
enum BuildOptionsErrorKind {
    Annotations,
    ImagePath,
    ScalingMultiplier,
}

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

impl error::Error for BuildOptionsError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&error::Error> {
        self.cause.as_ref().map(|cause| cause.as_ref())
    }
}

fn read_command() -> Result<Options> {
    use clap::ArgGroup;

    let text_group = ArgGroup::with_name("text_group")
        .args(&["caption", "bottom", "top", "middle"])
        .multiple(true);

    let encoding_group = ArgGroup::with_name("enc_group").args(&["encoding", "jpg", "png"]);

    let app =
        clap_app!(annatar =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg image: +required "Path to an image to be annotated")
        (@arg caption: "A message to be added to the bottom of the image")
        (@arg bottom: -b --bottom +takes_value "A message to be added to the bottom of the image")
        (@arg top: -t --top +takes_value "A message to be added to the top of the image")
        (@arg middle: -m --middle +takes_value "A message to be added to the middle of the image")
        (@arg output: -o --output +takes_value
            "Sets an output path for the new image (default: <image path>/<image name>.ann.<ext>)")
        (@arg scale: -s --scale +takes_value "Sets the scale multiplier for annotations")
        (@arg font: -f --font +takes_value "Sets the path of the font to be used (default: Impact)")
        (@arg debug: -d --debug "Save edge detection ... thing to disk")
        (@arg jpg: --jpg "Set JPG mode (default)")
        (@arg png: --png "Set PNG mode")
    );

    // Much easier to set up argument groups outside macro.
    let app = app.group(text_group).group(encoding_group);

    let matches = app.get_matches();
    let mut options = OptionsBuilder::new();

    options.base_image = Some(matches.value_of("image").unwrap().to_string());
    options.output_path = matches.value_of("output").map(|s| s.to_string());

    if let Some(scale_multiplier) = matches.value_of("scale") {
        let multiplier = scale_multiplier.parse::<f32>().map_err(|e| {
            BuildOptionsError {
                kind: BuildOptionsErrorKind::ScalingMultiplier,
                description: Cow::from("Scaling multiplier must be a decimal value"),
                cause: Some(Box::new(e)),
            }
        })?;
        options.scale_mult = multiplier;
    }

    if let Some(font_path) = matches.value_of("font") {
        options.font_path = Cow::from(font_path.to_string());
    }

    if let Some(caption) = matches.value_of("caption") {
        options.annotations.push(Annotation::bottom(caption));
    }

    if let Some(caption) = matches.value_of("top") {
        options.annotations.push(Annotation::top(caption));
    }

    if let Some(caption) = matches.value_of("middle") {
        options.annotations.push(Annotation::middle(caption));
    }

    if let Some(caption) = matches.value_of("bottom") {
        options.annotations.push(Annotation::bottom(caption));
    }

    options.output_format = {
        if matches.is_present("jpg") {
            OutputFormat::Jpg
        } else {
            OutputFormat::Png
        }
    };

    options.debug = matches.is_present("debug");

    options.build()
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}

fn create_output_file_path(input_path: &Path, output_format: OutputFormat) -> PathBuf {
    // I unwrap this because clap already converted it to a string, implying it's valid utf-8.
    let mut file_name = input_path
        .file_name()
        .unwrap()
        .to_str()
        .unwrap()
        .to_string();

    if let Some(last_segment_idx) = file_name.rfind('.') {
        file_name.truncate(last_segment_idx);
    }

    match output_format {
        OutputFormat::Png => file_name.push_str("-annotated.png"),
        _ => file_name.push_str("-annotated.jpg"),
    }

    file_name.into()
}
<|endoftext|>