<commit_before>//! Implementations of the callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
#![allow(deprecated)] // keysyms

use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::{ButtonState, KeyboardModifiers, KeyState, KeyboardLed, ScrollAxis, Size, Point, Geometry,
                     ResizeEdge, ViewState, VIEW_ACTIVATED, VIEW_FULLSCREEN, VIEW_RESIZING, VIEW_MAXIMIZED,
                     MOD_NONE, RESIZE_LEFT, RESIZE_RIGHT, RESIZE_TOP, RESIZE_BOTTOM};
use rustwlc::input::{pointer, keyboard};

use super::keys::{self, KeyPress, KeyEvent};
use super::layout::{try_lock_tree, try_lock_action, Action, ContainerType,
                    MovementError, TreeError, FocusError};
use super::layout::commands::set_performing_action;
use super::lua::{self, LuaQuery};

/// If the event is handled by way-cooler
const EVENT_BLOCKED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

const LEFT_CLICK: u32 = 0x110;
const RIGHT_CLICK: u32 = 0x111;

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    if let Ok(mut tree) = try_lock_tree() {
        let result = tree.add_output(output).and_then(|_|{
            tree.switch_to_workspace(&"1")
                .map(|_| tree.layout_active_of(ContainerType::Output))
        });
        match result {
            // If the output exists, we just couldn't add it to the tree because
            // it's already there. That's OK
            Ok(_) | Err(TreeError::OutputExists(_)) => true,
            _ => false
        }
    } else {
        false
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                                old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
           output, *old_size_ptr, *new_size_ptr);
    // Update the resolution of the output and its children
    let scale = 1;
    output.set_resolution(*new_size_ptr, scale);
    if let Ok(mut tree) = try_lock_tree() {
        tree.layout_active_of(ContainerType::Output)
            .expect("Could not layout active output");
    }
}

pub extern fn view_created(view: WlcView) -> bool {
    debug!("view_created: {:?}: \"{}\"", view, view.get_title());
    // TODO Remove this hack
    let class_name = view.get_class();
    if class_name.as_str() == "Background" {
        debug!("Setting background: {}", view.get_title());
        view.send_to_back();
        view.set_mask(1);
        let output = view.get_output();
        let resolution = output.get_resolution()
            .expect("Couldn't get output resolution");
        let fullscreen = Geometry {
            origin: Point { x: 0, y: 0 },
            size: resolution
        };
        view.set_geometry(ResizeEdge::empty(), fullscreen);
        if let Ok(mut tree) = try_lock_tree() {
            let outputs = tree.outputs();
            return tree.add_background(view, outputs.as_slice()).is_ok();
        }
        return false
    }  else if class_name.as_str() == "lemonbar"{
        // TODO Move this hack, probably could live somewhere else
        if let Ok(mut tree) = try_lock_tree() {
            for output in WlcOutput::list() {
                tree.add_bar(view, output).unwrap_or_else(|_| {
                    warn!("Could not add bar {:#?} to output {:#?}", view, output);
                });
            }
            return true;
        }
    }
    if let Ok(mut tree) = try_lock_tree() {
        let result = tree.add_view(view).and_then(|_| {
            view.set_state(VIEW_MAXIMIZED, true);
            match tree.set_active_view(view) {
                // If blocked by fullscreen, we don't focus on purpose
                Err(TreeError::Focus(FocusError::BlockedByFullscreen(_, _))) => Ok(()),
                result => result
            }
        });
        if result.is_err() {
            warn!("Could not add {:?}. Reason: {:?}", view, result);
        }
        result.is_ok()
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = try_lock_tree() {
        tree.remove_view(view).unwrap_or_else(|err| {
            match err {
                TreeError::ViewNotFound(_) => {},
                _ => {
                    error!("Error in view_destroyed: {:?}", err);
                }
            }
        });
    } else {
        error!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    if let Ok(mut tree) = try_lock_tree() {
        match tree.set_active_view(current) {
            Ok(_) => {},
            Err(err) => {
                error!("Could not set {:?} to be active view: {:?}", current, err);
            }
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_state(view: WlcView, state: ViewState, toggle: bool) {
    trace!("Setting {:?} to state {:?}", view, state);
    if state == VIEW_FULLSCREEN {
        if let Ok(mut tree) = try_lock_tree() {
            if let Some(id) = tree.lookup_view(view) {
                tree.set_fullscreen(id, toggle)
                    .expect("The ID was related to a non-view, somehow!");
                match tree.container_in_active_workspace(id) {
                    Ok(true) => {
                        tree.layout_active_of(ContainerType::Workspace)
                            .unwrap_or_else(|err| {
                                error!("Could not layout active workspace for view {:?}: {:?}",
                                        view, err)
                            });
                    },
                    Ok(false) => {},
                    Err(err) => error!("Could not set {:?} fullscreen: {:?}", view, err)
                }
            } else {
                warn!("Could not find view {:?} in tree", view);
            }
        }
    }
}

pub extern fn view_request_move(view: WlcView, _dest: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.set_active_view(view) {
            error!("view_request_move error: {:?}", err);
        }
    }
}

pub extern fn view_request_resize(view: WlcView, edge: ResizeEdge, point: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        match try_lock_action() {
            Ok(guard) => {
                if guard.is_some() {
                    if let Some(id) = tree.lookup_view(view) {
                        if let Err(err) = tree.resize_container(id, edge, *point) {
                            error!("Problem: Command returned error: {:#?}", err);
                        }
                    }
                }
            },
            _ => {}
        }
    }
}

pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                           key: u32, state: KeyState) -> bool {
    let empty_mods: KeyboardModifiers = KeyboardModifiers {
            mods: MOD_NONE,
            leds: KeyboardLed::empty()
    };
    let sym = keyboard::get_keysym_for_key(key, empty_mods);
    let press = KeyPress::new(mods.mods, sym);

    if state == KeyState::Pressed {
        if let Some(action) = keys::get(&press) {
            info!("[key] Found an action for {}, blocking event", press);
            match action {
                KeyEvent::Command(func) => {
                    func();
                },
                KeyEvent::Lua => {
                    match lua::send(LuaQuery::HandleKey(press)) {
                        Ok(_) => {},
                        Err(err) => {
                            // We may want to wait for Lua's reply from
                            // keypresses; for example if the table is tampered
                            // with or Lua is restarted or Lua has an error.
                            // ATM Lua asynchronously logs this but in the future
                            // an error popup/etc is a good idea.
                            error!("Error sending keypress: {:?}", err);
                        }
                    }
                }
            }
            return EVENT_BLOCKED
        }
    }

    return EVENT_PASS_THROUGH
}

pub extern fn view_request_geometry(_view: WlcView, _geometry: &Geometry) {
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, button: u32,
                             state: ButtonState, point: &Point) -> bool {
    if state == ButtonState::Pressed {
        let mouse_mod = keys::mouse_modifier();
        if button == LEFT_CLICK && !view.is_root() {
            info!("User left clicked w/ mods \"{:?}\" on {:?}", mods, view);
            if let Ok(mut tree) = try_lock_tree() {
                tree.set_active_view(view).unwrap_or_else(|_| {
                    // still focus on view, even if not in tree.
                    view.focus();
                });
                if mods.mods.contains(mouse_mod) {
                    let action = Action {
                        view: view,
                        grab: *point,
                        edges: ResizeEdge::empty()
                    };
                    set_performing_action(Some(action));
                }
            }
        } else if button == RIGHT_CLICK && !view.is_root() {
            info!("User right clicked w/ mods \"{:?}\" on {:?}", mods, view);
            if let Ok(mut tree) = try_lock_tree() {
                tree.set_active_view(view).ok();
            }
            if mods.mods.contains(mouse_mod) {
                let action = Action {
                    view: view,
                    grab: *point,
                    edges: ResizeEdge::empty()
                };
                set_performing_action(Some(action));
                let geometry = view.get_geometry()
                    .expect("Could not get geometry of the view");
                let halfw = geometry.origin.x + geometry.size.w as i32 / 2;
                let halfh = geometry.origin.y + geometry.size.h as i32 / 2;

                {
                    let mut action: Action = try_lock_action().ok().and_then(|guard| *guard)
                        .unwrap_or(Action {
                            view: view,
                            grab: *point,
                            edges: ResizeEdge::empty()
                        });
                    let flag_x = if point.x < halfw {
                        RESIZE_LEFT
                    } else if point.x > halfw {
                        RESIZE_RIGHT
                    } else {
                        ResizeEdge::empty()
                    };

                    let flag_y = if point.y < halfh {
                        RESIZE_TOP
                    } else if point.y > halfh {
                        RESIZE_BOTTOM
                    } else {
                        ResizeEdge::empty()
                    };

                    action.edges = flag_x | flag_y;
                    set_performing_action(Some(action));
                }
                view.set_state(VIEW_RESIZING, true);
                return EVENT_BLOCKED
            }
        }
    } else {
        if let Ok(lock) = try_lock_action() {
            let unknown = format!("unknown ({})", button);
            info!("User released {:?} mouse button",
                  match button {
                      RIGHT_CLICK => "right",
                      LEFT_CLICK => "left",
                      _ => unknown.as_str()
                  });
            match *lock {
                Some(action) => {
                    let view = action.view;
                    if view.get_state().contains(VIEW_RESIZING) {
                        view.set_state(VIEW_RESIZING, false);
                    }
                },
                _ => {}
            }
        }
        set_performing_action(None);
    }
    EVENT_PASS_THROUGH
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, _axis: ScrollAxis,
                         _heights: [f64; 2]) -> bool {
    EVENT_PASS_THROUGH
}

pub extern fn pointer_motion(view: WlcView, _time: u32, point: &Point) -> bool {
    let mut result = EVENT_PASS_THROUGH;
    let mut maybe_action = None;
    {
        if let Ok(action_lock) = try_lock_action() {
            maybe_action = action_lock.clone();
        }
    }
    match maybe_action {
        None => result = EVENT_PASS_THROUGH,
        Some(action) => {
            if action.edges.bits() != 0 {
                if let Ok(mut tree) = try_lock_tree() {
                    // TODO Change to id of _view
                    // Need to implement a map of view to uuid first though...
                    if let Some(active_id) = tree.lookup_view(view) {
                        match tree.resize_container(active_id, action.edges, *point) {
                            // Return early here to not set the pointer
                            Ok(_) => return EVENT_BLOCKED,
                            Err(err) => error!("Error: {:#?}", err)
                        }
                    }
                }
            } else {
                if let Ok(mut tree) = try_lock_tree() {
                    match tree.try_drag_active(*point) {
                        Ok(_) => result = EVENT_BLOCKED,
                        Err(TreeError::PerformingAction(_)) |
                        Err(TreeError::Movement(MovementError::NotFloating(_))) => result = EVENT_PASS_THROUGH,
                        Err(err) => {
                            error!("Error: {:#?}", err);
                            result = EVENT_PASS_THROUGH
                        }
                    }
                }
            }
        }
    }
    pointer::set_position(*point);
    result
}

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
    keys::init();
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
    if let Ok(mut tree) = try_lock_tree() {
        if tree.destroy_tree().is_err() {
            error!("Could not destroy tree");
        }
    }

}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<commit_msg>Fixked how checking for bar program name<commit_after>//! Implementations of the callbacks exposed by wlc.
//! These functions are the main entry points into Way Cooler from user action.
#![allow(deprecated)] // keysyms

use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::{ButtonState, KeyboardModifiers, KeyState, KeyboardLed, ScrollAxis, Size, Point, Geometry,
                     ResizeEdge, ViewState, VIEW_ACTIVATED, VIEW_FULLSCREEN, VIEW_RESIZING, VIEW_MAXIMIZED,
                     MOD_NONE, RESIZE_LEFT, RESIZE_RIGHT, RESIZE_TOP, RESIZE_BOTTOM};
use rustwlc::input::{pointer, keyboard};

use super::keys::{self, KeyPress, KeyEvent};
use super::layout::{try_lock_tree, try_lock_action, Action, ContainerType,
                    MovementError, TreeError, FocusError};
use super::layout::commands::set_performing_action;
use super::lua::{self, LuaQuery};

/// If the event is handled by way-cooler
const EVENT_BLOCKED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

const LEFT_CLICK: u32 = 0x110;
const RIGHT_CLICK: u32 = 0x111;

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    if let Ok(mut tree) = try_lock_tree() {
        let result = tree.add_output(output).and_then(|_|{
            tree.switch_to_workspace(&"1")
                .map(|_| tree.layout_active_of(ContainerType::Output))
        });
        match result {
            // If the output exists, we just couldn't add it to the tree because
            // it's already there. That's OK
            Ok(_) | Err(TreeError::OutputExists(_)) => true,
            _ => false
        }
    } else {
        false
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                                old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
           output, *old_size_ptr, *new_size_ptr);
    // Update the resolution of the output and its children
    let scale = 1;
    output.set_resolution(*new_size_ptr, scale);
    if let Ok(mut tree) = try_lock_tree() {
        tree.layout_active_of(ContainerType::Output)
            .expect("Could not layout active output");
    }
}

pub extern fn view_created(view: WlcView) -> bool {
    debug!("view_created: {:?}: \"{}\"", view, view.get_title());
    // TODO Remove this hack
    if view.get_class().as_str() == "Background" {
        debug!("Setting background: {}", view.get_title());
        view.send_to_back();
        view.set_mask(1);
        let output = view.get_output();
        let resolution = output.get_resolution()
            .expect("Couldn't get output resolution");
        let fullscreen = Geometry {
            origin: Point { x: 0, y: 0 },
            size: resolution
        };
        view.set_geometry(ResizeEdge::empty(), fullscreen);
        if let Ok(mut tree) = try_lock_tree() {
            let outputs = tree.outputs();
            return tree.add_background(view, outputs.as_slice()).is_ok();
        }
        return false
    }  else if view.get_title().as_str() == "lemonbar"{
        // TODO Move this hack, probably could live somewhere else
        if let Ok(mut tree) = try_lock_tree() {
            for output in WlcOutput::list() {
                tree.add_bar(view, output).unwrap_or_else(|_| {
                    warn!("Could not add bar {:#?} to output {:#?}", view, output);
                });
            }
            return true;
        }
    }
    if let Ok(mut tree) = try_lock_tree() {
        let result = tree.add_view(view).and_then(|_| {
            view.set_state(VIEW_MAXIMIZED, true);
            match tree.set_active_view(view) {
                // If blocked by fullscreen, we don't focus on purpose
                Err(TreeError::Focus(FocusError::BlockedByFullscreen(_, _))) => Ok(()),
                result => result
            }
        });
        if result.is_err() {
            warn!("Could not add {:?}. Reason: {:?}", view, result);
        }
        result.is_ok()
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = try_lock_tree() {
        tree.remove_view(view).unwrap_or_else(|err| {
            match err {
                TreeError::ViewNotFound(_) => {},
                _ => {
                    error!("Error in view_destroyed: {:?}", err);
                }
            }
        });
    } else {
        error!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    if let Ok(mut tree) = try_lock_tree() {
        match tree.set_active_view(current) {
            Ok(_) => {},
            Err(err) => {
                error!("Could not set {:?} to be active view: {:?}", current, err);
            }
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_state(view: WlcView, state: ViewState, toggle: bool) {
    trace!("Setting {:?} to state {:?}", view, state);
    if state == VIEW_FULLSCREEN {
        if let Ok(mut tree) = try_lock_tree() {
            if let Some(id) = tree.lookup_view(view) {
                tree.set_fullscreen(id, toggle)
                    .expect("The ID was related to a non-view, somehow!");
                match tree.container_in_active_workspace(id) {
                    Ok(true) => {
                        tree.layout_active_of(ContainerType::Workspace)
                            .unwrap_or_else(|err| {
                                error!("Could not layout active workspace for view {:?}: {:?}",
                                        view, err)
                            });
                    },
                    Ok(false) => {},
                    Err(err) => error!("Could not set {:?} fullscreen: {:?}", view, err)
                }
            } else {
                warn!("Could not find view {:?} in tree", view);
            }
        }
    }
}

pub extern fn view_request_move(view: WlcView, _dest: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.set_active_view(view) {
            error!("view_request_move error: {:?}", err);
        }
    }
}

pub extern fn view_request_resize(view: WlcView, edge: ResizeEdge, point: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        match try_lock_action() {
            Ok(guard) => {
                if guard.is_some() {
                    if let Some(id) = tree.lookup_view(view) {
                        if let Err(err) = tree.resize_container(id, edge, *point) {
                            error!("Problem: Command returned error: {:#?}", err);
                        }
                    }
                }
            },
            _ => {}
        }
    }
}

pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                           key: u32, state: KeyState) -> bool {
    let empty_mods: KeyboardModifiers = KeyboardModifiers {
            mods: MOD_NONE,
            leds: KeyboardLed::empty()
    };
    let sym = keyboard::get_keysym_for_key(key, empty_mods);
    let press = KeyPress::new(mods.mods, sym);

    if state == KeyState::Pressed {
        if let Some(action) = keys::get(&press) {
            info!("[key] Found an action for {}, blocking event", press);
            match action {
                KeyEvent::Command(func) => {
                    func();
                },
                KeyEvent::Lua => {
                    match lua::send(LuaQuery::HandleKey(press)) {
                        Ok(_) => {},
                        Err(err) => {
                            // We may want to wait for Lua's reply from
                            // keypresses; for example if the table is tampered
                            // with or Lua is restarted or Lua has an error.
                            // ATM Lua asynchronously logs this but in the future
                            // an error popup/etc is a good idea.
                            error!("Error sending keypress: {:?}", err);
                        }
                    }
                }
            }
            return EVENT_BLOCKED
        }
    }

    return EVENT_PASS_THROUGH
}

pub extern fn view_request_geometry(_view: WlcView, _geometry: &Geometry) {
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, button: u32,
                             state: ButtonState, point: &Point) -> bool {
    if state == ButtonState::Pressed {
        let mouse_mod = keys::mouse_modifier();
        if button == LEFT_CLICK && !view.is_root() {
            info!("User left clicked w/ mods \"{:?}\" on {:?}", mods, view);
            if let Ok(mut tree) = try_lock_tree() {
                tree.set_active_view(view).unwrap_or_else(|_| {
                    // still focus on view, even if not in tree.
                    view.focus();
                });
                if mods.mods.contains(mouse_mod) {
                    let action = Action {
                        view: view,
                        grab: *point,
                        edges: ResizeEdge::empty()
                    };
                    set_performing_action(Some(action));
                }
            }
        } else if button == RIGHT_CLICK && !view.is_root() {
            info!("User right clicked w/ mods \"{:?}\" on {:?}", mods, view);
            if let Ok(mut tree) = try_lock_tree() {
                tree.set_active_view(view).ok();
            }
            if mods.mods.contains(mouse_mod) {
                let action = Action {
                    view: view,
                    grab: *point,
                    edges: ResizeEdge::empty()
                };
                set_performing_action(Some(action));
                let geometry = view.get_geometry()
                    .expect("Could not get geometry of the view");
                let halfw = geometry.origin.x + geometry.size.w as i32 / 2;
                let halfh = geometry.origin.y + geometry.size.h as i32 / 2;

                {
                    let mut action: Action = try_lock_action().ok().and_then(|guard| *guard)
                        .unwrap_or(Action {
                            view: view,
                            grab: *point,
                            edges: ResizeEdge::empty()
                        });
                    let flag_x = if point.x < halfw {
                        RESIZE_LEFT
                    } else if point.x > halfw {
                        RESIZE_RIGHT
                    } else {
                        ResizeEdge::empty()
                    };

                    let flag_y = if point.y < halfh {
                        RESIZE_TOP
                    } else if point.y > halfh {
                        RESIZE_BOTTOM
                    } else {
                        ResizeEdge::empty()
                    };

                    action.edges = flag_x | flag_y;
                    set_performing_action(Some(action));
                }
                view.set_state(VIEW_RESIZING, true);
                return EVENT_BLOCKED
            }
        }
    } else {
        if let Ok(lock) = try_lock_action() {
            let unknown = format!("unknown ({})", button);
            info!("User released {:?} mouse button",
                  match button {
                      RIGHT_CLICK => "right",
                      LEFT_CLICK => "left",
                      _ => unknown.as_str()
                  });
            match *lock {
                Some(action) => {
                    let view = action.view;
                    if view.get_state().contains(VIEW_RESIZING) {
                        view.set_state(VIEW_RESIZING, false);
                    }
                },
                _ => {}
            }
        }
        set_performing_action(None);
    }
    EVENT_PASS_THROUGH
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, _axis: ScrollAxis,
                         _heights: [f64; 2]) -> bool {
    EVENT_PASS_THROUGH
}

pub extern fn pointer_motion(view: WlcView, _time: u32, point: &Point) -> bool {
    let mut result = EVENT_PASS_THROUGH;
    let mut maybe_action = None;
    {
        if let Ok(action_lock) = try_lock_action() {
            maybe_action = action_lock.clone();
        }
    }
    match maybe_action {
        None => result = EVENT_PASS_THROUGH,
        Some(action) => {
            if action.edges.bits() != 0 {
                if let Ok(mut tree) = try_lock_tree() {
                    // TODO Change to id of _view
                    // Need to implement a map of view to uuid first though...
                    if let Some(active_id) = tree.lookup_view(view) {
                        match tree.resize_container(active_id, action.edges, *point) {
                            // Return early here to not set the pointer
                            Ok(_) => return EVENT_BLOCKED,
                            Err(err) => error!("Error: {:#?}", err)
                        }
                    }
                }
            } else {
                if let Ok(mut tree) = try_lock_tree() {
                    match tree.try_drag_active(*point) {
                        Ok(_) => result = EVENT_BLOCKED,
                        Err(TreeError::PerformingAction(_)) |
                        Err(TreeError::Movement(MovementError::NotFloating(_))) => result = EVENT_PASS_THROUGH,
                        Err(err) => {
                            error!("Error: {:#?}", err);
                            result = EVENT_PASS_THROUGH
                        }
                    }
                }
            }
        }
    }
    pointer::set_position(*point);
    result
}

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
    keys::init();
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
    if let Ok(mut tree) = try_lock_tree() {
        if tree.destroy_tree().is_err() {
            error!("Could not destroy tree");
        }
    }

}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate linkify;

extern crate test;

use linkify::LinkFinder;
use test::Bencher;

#[bench]
fn no_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        link_finder.find(
            "This is a text with no links in it. But: it has a colon.\
             Lorem ipsum dolor sit amet, consectetur adipiscing elit.\
             Curabitur luctus tincidunt diam.").count()
    });
}

#[bench]
fn some_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        link_finder.find(
            "This is a text with links in it. Here's one: http://www.example.com/. \
             How about another one? (Maybe like this http://example.com/foo_(bar)) \
             a+b://example.com/foo+bar+baz").count()
    });
}

#[bench]
fn heaps_of_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        link_finder.find(
            "http://www.example.com/a, http://www.example.com/b, http://www.example.com/c \
             http://www.example.com/a: http://www.example.com/b: http://www.example.com/c \
             http://www.example.com/a http://www.example.com/b http://www.example.com/c \
             http://www.example.com/a< http://www.example.com/b< http://www.example.com/c<").count()
    });
}
<commit_msg>Add assertions to benches<commit_after>#![feature(test)]
extern crate linkify;

extern crate test;

use linkify::LinkFinder;
use test::Bencher;

#[bench]
fn no_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        let links = link_finder.find(
            "This is a text with no links in it. But: it has a colon.\
             Lorem ipsum dolor sit amet, consectetur adipiscing elit.\
             Curabitur luctus tincidunt diam.");
        assert_eq!(links.count(), 0);
    });
}

#[bench]
fn some_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        let links = link_finder.find(
            "This is a text with links in it. Here's one: http://www.example.com/. \
             How about another one? (Maybe like this http://example.com/foo_(bar)) \
             a+b://example.com/foo+bar+baz");
        assert_eq!(links.count(), 3);
    });
}

#[bench]
fn heaps_of_links(b: &mut Bencher) {
    let link_finder = LinkFinder::new();
    b.iter(|| {
        let links = link_finder.find(
            "http://www.example.com/a, http://www.example.com/b, http://www.example.com/c \
             http://www.example.com/a: http://www.example.com/b: http://www.example.com/c \
             http://www.example.com/a http://www.example.com/b http://www.example.com/c \
             http://www.example.com/a< http://www.example.com/b< http://www.example.com/c<");
        assert_eq!(links.count(), 12);
    });
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::fmt::Debug;
use cidr::Cidr;

#[derive(Debug)]
pub struct CidrTree<T> where T: Debug {
    zero: Option<Box<CidrTree<T>>>,
    one: Option<Box<CidrTree<T>>>,
    data: Option<T>,
}

impl<T> CidrTree<T> where T: Debug {
    pub fn new() -> CidrTree<T> {
        CidrTree {
            zero: None,
            one: None,
            data: None,
        }
    }

    pub fn new_with_data(data: T) -> CidrTree<T> {
        CidrTree {
            zero: None,
            one: None,
            data: Some(data),
        }
    }

    // Returns a vector of all the data that applies the queried CIDR
    pub fn get(&self, cidr: &Cidr) -> Vec<&T> {
        let mut results = Vec::<&T>::new();

        // I might have something to contribute
        if let Some(ref d) = self.data {
            results.push(d);
        }
        let next_cidr = cidr.next();
        println!(" cidr: {:?}, msbit: {:?}", cidr, cidr.msbit());
        match cidr.msbit() {
            0 => {
                match self.zero {
                    Some(ref child) => {
                        results.extend(child.get(&next_cidr));
                    },
                    None => {}
                }
            },
            _ => {
                match self.one {
                    Some(ref child) => {
                        results.extend(child.get(&next_cidr));
                    },
                    None => {}
                }
            },
        };
        results
    }

    pub fn get_from_str(&self, cidr: &str) -> Vec<&T> {
        println!("getting {:?}", Cidr::from_str(cidr).unwrap());
        self.get(&Cidr::from_str(cidr).unwrap())
    }

    pub fn insert(&mut self, cidr: &Cidr, data: Option<T>) {
        // Search is over; this node is where the data goes
        if cidr.length == 0 {
            self.data = data;
            return;
        }

        // Next cidr is the incoming cidr shifted left by one
        let next_cidr = cidr.next();

        // TODO repetitive code
        match cidr.msbit() {
            0 => {
                match self.zero {
                    Some(ref mut child) => {
                        child.insert(&next_cidr, data);
                    },
                    None => {
                        let mut child = CidrTree::<T>::new();
                        child.insert(&next_cidr, data);
                        self.zero = Some(Box::new(child));
                    },
                }
            },
            _ => {
                match self.one {
                    Some(ref mut child) => {
                        child.insert(&next_cidr, data);
                    },
                    None => {
                        let mut child = CidrTree::<T>::new();
                        child.insert(&next_cidr, data);
                        self.one = Some(Box::new(child));
                    },
                }
            },
        }
    }
}

#[test]
fn test_insert_v4() {
    let mut t = CidrTree::<String>::new();

    t.insert(&Cidr::from_str("128.0.0.0/1").unwrap(), Some("first".to_string()));

    assert!(t.get_from_str(&"1.0.0.0").is_empty());
    assert!(t.get_from_str(&"128.0.0.0").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0")[0] == "first");
    assert!(t.get_from_str(&"128.1.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0/8").len() == 1);

    t.insert(&Cidr::from_str("255.0.0.0/2").unwrap(), Some("second".to_string()));

    assert!(t.get_from_str(&"1.0.0.0").is_empty());
    assert!(t.get_from_str(&"128.0.0.0").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 2);
    assert!(t.get_from_str(&"128.0.0.0")[0] == "first");
    assert!(t.get_from_str(&"128.1.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0/8").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 2);
    assert!(t.get_from_str(&"255.1.0.0").len() == 2);
    assert!(t.get_from_str(&"255.0.0.0/8").len() == 2);
}

#[test]
fn test_insert_v6() {
    let mut t = CidrTree::<String>::new();

    t.insert(&Cidr::from_str("8000:0:0:0::/1").unwrap(), Some("first".to_string()));
    assert!(t.get_from_str(&"0001:0:0:0::").is_empty());
    assert!(t.get_from_str(&"8000::").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 1);
    assert!(t.get_from_str(&"8000::1").len() == 1);
    assert!(t.get_from_str(&"8000::/8").len() == 1);

    t.insert(&Cidr::from_str("F000:0:0:0::/2").unwrap(), Some("second".to_string()));

    assert!(t.get_from_str(&"0001:0:0:0::").is_empty());
    assert!(t.get_from_str(&"8000::").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 2);
    assert!(t.get_from_str(&"8000::1").len() == 1);
    assert!(t.get_from_str(&"8000::/8").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 2);
    assert!(t.get_from_str(&"F800::").len() == 2);
    assert!(t.get_from_str(&"F000::/8").len() == 2);
}
<commit_msg>Remove debug prints<commit_after>use std::str::FromStr;
use std::fmt::Debug;
use cidr::Cidr;

#[derive(Debug)]
pub struct CidrTree<T> where T: Debug {
    zero: Option<Box<CidrTree<T>>>,
    one: Option<Box<CidrTree<T>>>,
    data: Option<T>,
}

impl<T> CidrTree<T> where T: Debug {
    pub fn new() -> CidrTree<T> {
        CidrTree {
            zero: None,
            one: None,
            data: None,
        }
    }

    pub fn new_with_data(data: T) -> CidrTree<T> {
        CidrTree {
            zero: None,
            one: None,
            data: Some(data),
        }
    }

    // Returns a vector of all the data that applies the queried CIDR
    pub fn get(&self, cidr: &Cidr) -> Vec<&T> {
        let mut results = Vec::<&T>::new();

        // I might have something to contribute
        if let Some(ref d) = self.data {
            results.push(d);
        }
        let next_cidr = cidr.next();
        match cidr.msbit() {
            0 => {
                match self.zero {
                    Some(ref child) => {
                        results.extend(child.get(&next_cidr));
                    },
                    None => {}
                }
            },
            _ => {
                match self.one {
                    Some(ref child) => {
                        results.extend(child.get(&next_cidr));
                    },
                    None => {}
                }
            },
        };
        results
    }

    pub fn get_from_str(&self, cidr: &str) -> Vec<&T> {
        self.get(&Cidr::from_str(cidr).unwrap())
    }

    pub fn insert(&mut self, cidr: &Cidr, data: Option<T>) {
        // Search is over; this node is where the data goes
        if cidr.length == 0 {
            self.data = data;
            return;
        }

        // Next cidr is the incoming cidr shifted left by one
        let next_cidr = cidr.next();

        // TODO repetitive code
        match cidr.msbit() {
            0 => {
                match self.zero {
                    Some(ref mut child) => {
                        child.insert(&next_cidr, data);
                    },
                    None => {
                        let mut child = CidrTree::<T>::new();
                        child.insert(&next_cidr, data);
                        self.zero = Some(Box::new(child));
                    },
                }
            },
            _ => {
                match self.one {
                    Some(ref mut child) => {
                        child.insert(&next_cidr, data);
                    },
                    None => {
                        let mut child = CidrTree::<T>::new();
                        child.insert(&next_cidr, data);
                        self.one = Some(Box::new(child));
                    },
                }
            },
        }
    }
}

#[test]
fn test_insert_v4() {
    let mut t = CidrTree::<String>::new();

    t.insert(&Cidr::from_str("128.0.0.0/1").unwrap(), Some("first".to_string()));

    assert!(t.get_from_str(&"1.0.0.0").is_empty());
    assert!(t.get_from_str(&"128.0.0.0").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0")[0] == "first");
    assert!(t.get_from_str(&"128.1.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0/8").len() == 1);

    t.insert(&Cidr::from_str("255.0.0.0/2").unwrap(), Some("second".to_string()));

    assert!(t.get_from_str(&"1.0.0.0").is_empty());
    assert!(t.get_from_str(&"128.0.0.0").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 2);
    assert!(t.get_from_str(&"128.0.0.0")[0] == "first");
    assert!(t.get_from_str(&"128.1.0.0").len() == 1);
    assert!(t.get_from_str(&"128.0.0.0/8").len() == 1);
    assert!(t.get_from_str(&"255.0.0.0").len() == 2);
    assert!(t.get_from_str(&"255.1.0.0").len() == 2);
    assert!(t.get_from_str(&"255.0.0.0/8").len() == 2);
}

#[test]
fn test_insert_v6() {
    let mut t = CidrTree::<String>::new();

    t.insert(&Cidr::from_str("8000:0:0:0::/1").unwrap(), Some("first".to_string()));
    assert!(t.get_from_str(&"0001:0:0:0::").is_empty());
    assert!(t.get_from_str(&"8000::").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 1);
    assert!(t.get_from_str(&"8000::1").len() == 1);
    assert!(t.get_from_str(&"8000::/8").len() == 1);

    t.insert(&Cidr::from_str("F000:0:0:0::/2").unwrap(), Some("second".to_string()));

    assert!(t.get_from_str(&"0001:0:0:0::").is_empty());
    assert!(t.get_from_str(&"8000::").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 2);
    assert!(t.get_from_str(&"8000::1").len() == 1);
    assert!(t.get_from_str(&"8000::/8").len() == 1);
    assert!(t.get_from_str(&"F000::").len() == 2);
    assert!(t.get_from_str(&"F800::").len() == 2);
    assert!(t.get_from_str(&"F000::/8").len() == 2);
}
<|endoftext|>


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

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // No device nodes means no metadata
    if devnodes.is_empty() {
        return Ok(None);
    }

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if bda.is_none() {
            continue;
        }
        let bda = bda.expect("unreachable if bda is None");

        if *bda.pool_uuid() != pool_uuid {
            continue;
        }
        bdas.push((devnode, bda));
    }

    // We may have had no devices with BDAs for this pool, so return if no BDAs.
    if bdas.is_empty() {
        return Ok(None);
    }

    // Get a most recent BDA
    let &(_, ref most_recent_bda) = bdas.iter()
        .max_by_key(|p| p.1.last_update_time())
        .expect("bdas is not empty, must have a max");

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = most_recent_bda.last_update_time();
    if most_recent_time.is_none() {
        return Ok(None);
    }

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|p| p.1.last_update_time() == most_recent_time) {

        let f = OpenOptions::new().read(true).open(devnode);
        if f.is_err() {
            continue;
        }
        let mut f = f.expect("f is not err");

        if let Ok(Some(data)) = bda.load_state(&mut f) {
            let json: serde_json::Result<PoolSave> = serde_json::from_slice(&data);
            if let Ok(pool) = json {
                return Ok(Some(pool));
            } else {
                continue;
            }
        } else {
            continue;
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get the blockdevs corresponding to this pool.
pub fn get_blockdevs(pool_save: &PoolSave, devnodes: &[PathBuf]) -> EngineResult<Vec<BlockDev>> {
    let segments = pool_save
        .flex_devs
        .meta_dev
        .iter()
        .chain(pool_save.flex_devs.thin_meta_dev.iter())
        .chain(pool_save.flex_devs.thin_data_dev.iter());

    let mut segment_table = HashMap::new();
    for seg in segments {
        segment_table
            .entry(seg.0.clone())
            .or_insert(vec![])
            .push((seg.1, seg.2))
    }

    let mut blockdevs = vec![];
    let mut devices = HashSet::new();
    for dev in devnodes {
        let device = try!(Device::from_str(&dev.to_string_lossy()));

        // If we've seen this device already, skip it.
        if !devices.insert(device) {
            continue;
        }

        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
        let bda = try!(bda.ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                     "no BDA found for Stratis device".into())));

        let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
            .sectors();

        // If size of device has changed and is less, then it is possible
        // that the segments previously allocated for this blockdev no
        // longer exist. If that is the case, RangeAllocator::new() will
        // return an error.
        let allocator =
            try!(RangeAllocator::new(actual_size,
                                     segment_table.get(bda.dev_uuid()).unwrap_or(&vec![])));

        blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
    }

    // Verify that blockdevs found match blockdevs recorded.
    let current_uuids: HashSet<_> = blockdevs.iter().map(|b| *b.uuid()).collect();
    let recorded_uuids: HashSet<_> = pool_save.block_devs.keys().map(|u| *u).collect();

    if current_uuids != recorded_uuids {
        let err_msg = "Recorded block dev UUIDs != discovered blockdev UUIDs";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if blockdevs.len() != current_uuids.len() {
        let err_msg = "Duplicate block devices found in environment";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok(blockdevs)
}
<commit_msg>Use and_then to streamline control flow in get_metadata<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/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // No device nodes means no metadata
    if devnodes.is_empty() {
        return Ok(None);
    }

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if bda.is_none() {
            continue;
        }
        let bda = bda.expect("unreachable if bda is None");

        if *bda.pool_uuid() != pool_uuid {
            continue;
        }
        bdas.push((devnode, bda));
    }

    // We may have had no devices with BDAs for this pool, so return if no BDAs.
    if bdas.is_empty() {
        return Ok(None);
    }

    // Get a most recent BDA
    let &(_, ref most_recent_bda) = bdas.iter()
        .max_by_key(|p| p.1.last_update_time())
        .expect("bdas is not empty, must have a max");

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = most_recent_bda.last_update_time();
    if most_recent_time.is_none() {
        return Ok(None);
    }

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|&&(_, ref bda)| bda.last_update_time() == most_recent_time) {

        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get the blockdevs corresponding to this pool.
pub fn get_blockdevs(pool_save: &PoolSave, devnodes: &[PathBuf]) -> EngineResult<Vec<BlockDev>> {
    let segments = pool_save
        .flex_devs
        .meta_dev
        .iter()
        .chain(pool_save.flex_devs.thin_meta_dev.iter())
        .chain(pool_save.flex_devs.thin_data_dev.iter());

    let mut segment_table = HashMap::new();
    for seg in segments {
        segment_table
            .entry(seg.0.clone())
            .or_insert(vec![])
            .push((seg.1, seg.2))
    }

    let mut blockdevs = vec![];
    let mut devices = HashSet::new();
    for dev in devnodes {
        let device = try!(Device::from_str(&dev.to_string_lossy()));

        // If we've seen this device already, skip it.
        if !devices.insert(device) {
            continue;
        }

        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
        let bda = try!(bda.ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                     "no BDA found for Stratis device".into())));

        let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
            .sectors();

        // If size of device has changed and is less, then it is possible
        // that the segments previously allocated for this blockdev no
        // longer exist. If that is the case, RangeAllocator::new() will
        // return an error.
        let allocator =
            try!(RangeAllocator::new(actual_size,
                                     segment_table.get(bda.dev_uuid()).unwrap_or(&vec![])));

        blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
    }

    // Verify that blockdevs found match blockdevs recorded.
    let current_uuids: HashSet<_> = blockdevs.iter().map(|b| *b.uuid()).collect();
    let recorded_uuids: HashSet<_> = pool_save.block_devs.keys().map(|u| *u).collect();

    if current_uuids != recorded_uuids {
        let err_msg = "Recorded block dev UUIDs != discovered blockdev UUIDs";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if blockdevs.len() != current_uuids.len() {
        let err_msg = "Duplicate block devices found in environment";
        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok(blockdevs)
}
<|endoftext|>


<commit_before>use imageflow_helpers::preludes::from_std::*;
use imageflow_types as s;

pub mod parsing;
mod layout;

use crate::sizing;
use crate::sizing::prelude::*;
use crate::ir4::parsing::*;
use crate::ir4::layout::*;

pub use layout::ConstraintResults;

pub fn process_constraint(source_w: i32, source_h: i32, constraint: &imageflow_types::Constraint) -> sizing::Result<ConstraintResults>{
    layout::Ir4Layout::process_constraint(source_w,source_h, constraint)
}

pub enum Ir4Command{
    Instructions(Box<Instructions>),
    Url(String),
    QueryString(String)
}

impl Ir4Command{
    pub fn parse(&self) -> sizing::Result<Ir4Result> {
        let (i, warn) = match *self {
            Ir4Command::Url(ref url) => parsing::parse_url(&::url::Url::from_str(url).expect("ImageResizer4 Url cannot be parsed into instructions: invalid URI")),
            Ir4Command::Instructions(ref i) => (**i, vec![]),
            Ir4Command::QueryString(ref s) => {
                let url = ::url::Url::from_str(&format!("https://fakeurl/img.jpg?{}", s)).expect("Must be a valid querystring, excluding ?");
                parsing::parse_url(&url)
            }
        };
        Ok(Ir4Result{
            parse_warnings: warn,
            parsed: i,
            steps: None,
            canvas: None
        })
    }

}

/// Minimal translation into framewise (delay as much as possible)
pub struct Ir4Translate{
    pub i: Ir4Command,
    pub decode_id: Option<i32>,
    pub encode_id: Option<i32>,
}

// If using trim.threshold, delayed expansion is required.

pub struct Ir4Result{
    pub parse_warnings: Vec<parsing::ParseWarning>,
    pub parsed: Instructions,
    pub steps: Option<Vec<s::Node>>,
    pub canvas: Option<AspectRatio>
}

impl Ir4Translate{

    pub fn get_decode_node(&self) -> Option<s::Node>{
        if let Some(id) = self.decode_id {
            Some(s::Node::Decode { io_id: id, commands: None })
        }else{
            None
        }
    }

    pub fn translate(&self) -> sizing::Result<Ir4Result> {
        let mut r = self.i.parse()?;
        let mut b = crate::ir4::layout::FramewiseBuilder::new();
        //Expand decoder early if trimming
        let delayed_id = if r.parsed.trim_whitespace_threshold.is_some() {
            if let Some(n) = self.get_decode_node() {
                b.add(n);
            }
            None
        } else {
            self.decode_id
        };
        // Add CropWhitespace
        if r.parsed.trim_whitespace_threshold.is_some(){
            b.add(s::Node::CropWhitespace {
                threshold: cmp::max(0,r.parsed.trim_whitespace_threshold.unwrap()) as u32,
                percent_padding: r.parsed.trim_whitespace_padding_percent.unwrap_or(0f64) as f32
            });
        }

        //delete whitespace from instructions
        let mut without_trimming: Instructions = r.parsed;
        without_trimming.trim_whitespace_padding_percent = None;
        without_trimming.trim_whitespace_threshold = None;

        b.add(s::Node::CommandString {
            kind: s::CommandStringKind::ImageResizer4,
            value: without_trimming.to_string(),
            decode: delayed_id,
            encode: self.encode_id
        });

        r.steps = Some(b.into_steps());
        Ok(r)
    }
}

pub struct Ir4SourceFrameInfo{
    pub w: i32,
    pub h: i32,
    pub fmt: s::PixelFormat,
    pub original_mime: Option<String>,
}

impl Ir4SourceFrameInfo{

    fn get_format_from_mime(&self) -> Option<OutputFormat>{
        self.original_mime.as_ref().and_then(|f|
                    match f.as_str(){
                        "image/jpeg" => Some(OutputFormat::Jpeg),
                        "image/png" => Some(OutputFormat::Png),
                        "image/gif" => Some(OutputFormat::Gif),
                        "image/webp" => Some(OutputFormat::Webp),
                        _ => None
                    })
    }
    fn get_format_from_frame(&self) -> OutputFormat{
        match self.fmt{
            s::PixelFormat::Bgr24 | s::PixelFormat::Bgr32 => OutputFormat::Jpeg,
            _ => OutputFormat::Png
        }
    }
}

/// Cannot expand decoder. use `Ir4Translate` for that.
pub struct Ir4Expand{
    pub i: Ir4Command,
    pub source: Ir4SourceFrameInfo,
    pub encode_id: Option<i32>,

}

impl Ir4Expand{

    pub fn get_decode_commands(&self) -> sizing::Result<Option<Vec<s::DecoderCommand>>> { //TODO: consider smallvec or generalizing decoder hints
        let i = self.i.parse()?.parsed;

        // Default to gamma correct
        let gamma_correct = i.down_colorspace != Some(ScalingColorspace::Srgb);

        let layout = self.get_layout(&i)?;
        let (from, to): (AspectRatio, AspectRatio) = layout.get_downscaling()?;

        let downscale_ratio = (f64::from(from.w) / f64::from(to.w)).min(f64::from(from.h) / f64::from(to.w));

        let preshrink_ratio = i.min_precise_scaling_ratio.unwrap_or(2.1f64) / downscale_ratio;

        let scaled_width = (f64::from(self.source.w) * preshrink_ratio).floor() as i64;
        let scaled_height = (f64::from(self.source.h) * preshrink_ratio).floor() as i64;
        if preshrink_ratio < 1f64 {
            let mut vec = Vec::with_capacity(2);
            vec.push(s::DecoderCommand::JpegDownscaleHints(s::JpegIDCTDownscaleHints {
                scale_luma_spatially: Some(gamma_correct),
                gamma_correct_for_srgb_during_spatial_luma_scaling: Some(gamma_correct),
                width: scaled_width,
                height: scaled_height
            }));
            if !gamma_correct{
                vec.push(s::DecoderCommand::WebPDecoderHints(s::WebPDecoderHints{
                    width: scaled_width as i32,
                    height: scaled_height as i32,
                }));
            }
            Ok(Some(vec))
        } else {
            Ok(None)
        }
    }

    pub fn get_canvas_size(&self) -> sizing::Result<AspectRatio>{
        let i = self.i.parse()?.parsed;
        let (_, layout) = self.get_layout(&i).unwrap().get_crop_and_layout().unwrap();
        Ok(layout.get_box(BoxTarget::CurrentCanvas))
    }

    pub fn get_layout(&self, i: &Instructions) -> sizing::Result<Ir4Layout> {
        if i.trim_whitespace_threshold.is_some() {
            return Err(sizing::LayoutError::ContentDependent);
        }
        Ok(layout::Ir4Layout::new(*i, self.source.w, self.source.h))
    }

    pub fn expand_steps(&self) -> sizing::Result<Ir4Result> {
        let mut r = self.i.parse()?;

        let layout = self.get_layout(&r.parsed)?;

        let mut b = FramewiseBuilder::new();
        r.canvas = Some(layout.add_steps(&mut b)?.canvas);

        if let Some(n) = self.get_encoder_node(&r.parsed) {
            b.add(n);
        }
        r.steps = Some(b.into_steps());
        Ok(r)

    }

    pub fn get_encoder_node(&self, i: &Instructions) -> Option<s::Node>{

        if let Some(id) = self.encode_id {

            let format = i.format.or_else(|| self.source.get_format_from_mime())
                .unwrap_or_else(|| self.source.get_format_from_frame());

            let encoder = match format {
                OutputFormat::Gif => s::EncoderPreset::Gif,
                OutputFormat::Jpeg if Some(true) == i.jpeg_turbo => s::EncoderPreset::LibjpegTurbo {
                    quality: Some(i.quality.unwrap_or(90)),
                    progressive: i.jpeg_progressive,
                    optimize_huffman_coding:  i.jpeg_progressive
                },
                OutputFormat::Jpeg=> s::EncoderPreset::Mozjpeg {
                    quality: Some(i.quality.unwrap_or(90) as u8),
                    progressive: i.jpeg_progressive
                },
                OutputFormat::Png if i.png_quality.is_some() => s::EncoderPreset::Pngquant {
                    quality: Some((i.png_min_quality.unwrap_or(i.png_quality.unwrap()), i.png_quality.unwrap())),
                    speed: i.png_quantization_speed,
                    maximum_deflate: i.png_max_deflate
                },
                OutputFormat::Png if i.png_libpng == Some(true) => s::EncoderPreset::Libpng {
                    depth: Some(if i.bgcolor_srgb.is_some() { s::PngBitDepth::Png24 } else { s::PngBitDepth::Png32 }),
                    zlib_compression: None,
                    matte: i.bgcolor_srgb.map(|sr| s::Color::Srgb(s::ColorSrgb::Hex(sr.to_rrggbbaa_string())))
                },
                OutputFormat::Png => s::EncoderPreset::Lodepng{
                    maximum_deflate: i.png_max_deflate
                },
                OutputFormat::Webp if i.webp_lossless == Some(true) => s::EncoderPreset::WebPLossless,
                OutputFormat::Webp => s::EncoderPreset::WebPLossy {
                    quality: i.webp_quality.unwrap_or(90f64) as f32
                },
            };
            Some(s::Node::Encode { io_id: id, preset: encoder })
        }else{
            None
        }
    }
}

<commit_msg>Max png.max_deflate=true  work with png.libpng=true<commit_after>use imageflow_helpers::preludes::from_std::*;
use imageflow_types as s;

pub mod parsing;
mod layout;

use crate::sizing;
use crate::sizing::prelude::*;
use crate::ir4::parsing::*;
use crate::ir4::layout::*;

pub use layout::ConstraintResults;

pub fn process_constraint(source_w: i32, source_h: i32, constraint: &imageflow_types::Constraint) -> sizing::Result<ConstraintResults>{
    layout::Ir4Layout::process_constraint(source_w,source_h, constraint)
}

pub enum Ir4Command{
    Instructions(Box<Instructions>),
    Url(String),
    QueryString(String)
}

impl Ir4Command{
    pub fn parse(&self) -> sizing::Result<Ir4Result> {
        let (i, warn) = match *self {
            Ir4Command::Url(ref url) => parsing::parse_url(&::url::Url::from_str(url).expect("ImageResizer4 Url cannot be parsed into instructions: invalid URI")),
            Ir4Command::Instructions(ref i) => (**i, vec![]),
            Ir4Command::QueryString(ref s) => {
                let url = ::url::Url::from_str(&format!("https://fakeurl/img.jpg?{}", s)).expect("Must be a valid querystring, excluding ?");
                parsing::parse_url(&url)
            }
        };
        Ok(Ir4Result{
            parse_warnings: warn,
            parsed: i,
            steps: None,
            canvas: None
        })
    }

}

/// Minimal translation into framewise (delay as much as possible)
pub struct Ir4Translate{
    pub i: Ir4Command,
    pub decode_id: Option<i32>,
    pub encode_id: Option<i32>,
}

// If using trim.threshold, delayed expansion is required.

pub struct Ir4Result{
    pub parse_warnings: Vec<parsing::ParseWarning>,
    pub parsed: Instructions,
    pub steps: Option<Vec<s::Node>>,
    pub canvas: Option<AspectRatio>
}

impl Ir4Translate{

    pub fn get_decode_node(&self) -> Option<s::Node>{
        if let Some(id) = self.decode_id {
            Some(s::Node::Decode { io_id: id, commands: None })
        }else{
            None
        }
    }

    pub fn translate(&self) -> sizing::Result<Ir4Result> {
        let mut r = self.i.parse()?;
        let mut b = crate::ir4::layout::FramewiseBuilder::new();
        //Expand decoder early if trimming
        let delayed_id = if r.parsed.trim_whitespace_threshold.is_some() {
            if let Some(n) = self.get_decode_node() {
                b.add(n);
            }
            None
        } else {
            self.decode_id
        };
        // Add CropWhitespace
        if r.parsed.trim_whitespace_threshold.is_some(){
            b.add(s::Node::CropWhitespace {
                threshold: cmp::max(0,r.parsed.trim_whitespace_threshold.unwrap()) as u32,
                percent_padding: r.parsed.trim_whitespace_padding_percent.unwrap_or(0f64) as f32
            });
        }

        //delete whitespace from instructions
        let mut without_trimming: Instructions = r.parsed;
        without_trimming.trim_whitespace_padding_percent = None;
        without_trimming.trim_whitespace_threshold = None;

        b.add(s::Node::CommandString {
            kind: s::CommandStringKind::ImageResizer4,
            value: without_trimming.to_string(),
            decode: delayed_id,
            encode: self.encode_id
        });

        r.steps = Some(b.into_steps());
        Ok(r)
    }
}

pub struct Ir4SourceFrameInfo{
    pub w: i32,
    pub h: i32,
    pub fmt: s::PixelFormat,
    pub original_mime: Option<String>,
}

impl Ir4SourceFrameInfo{

    fn get_format_from_mime(&self) -> Option<OutputFormat>{
        self.original_mime.as_ref().and_then(|f|
                    match f.as_str(){
                        "image/jpeg" => Some(OutputFormat::Jpeg),
                        "image/png" => Some(OutputFormat::Png),
                        "image/gif" => Some(OutputFormat::Gif),
                        "image/webp" => Some(OutputFormat::Webp),
                        _ => None
                    })
    }
    fn get_format_from_frame(&self) -> OutputFormat{
        match self.fmt{
            s::PixelFormat::Bgr24 | s::PixelFormat::Bgr32 => OutputFormat::Jpeg,
            _ => OutputFormat::Png
        }
    }
}

/// Cannot expand decoder. use `Ir4Translate` for that.
pub struct Ir4Expand{
    pub i: Ir4Command,
    pub source: Ir4SourceFrameInfo,
    pub encode_id: Option<i32>,

}

impl Ir4Expand{

    pub fn get_decode_commands(&self) -> sizing::Result<Option<Vec<s::DecoderCommand>>> { //TODO: consider smallvec or generalizing decoder hints
        let i = self.i.parse()?.parsed;

        // Default to gamma correct
        let gamma_correct = i.down_colorspace != Some(ScalingColorspace::Srgb);

        let layout = self.get_layout(&i)?;
        let (from, to): (AspectRatio, AspectRatio) = layout.get_downscaling()?;

        let downscale_ratio = (f64::from(from.w) / f64::from(to.w)).min(f64::from(from.h) / f64::from(to.w));

        let preshrink_ratio = i.min_precise_scaling_ratio.unwrap_or(2.1f64) / downscale_ratio;

        let scaled_width = (f64::from(self.source.w) * preshrink_ratio).floor() as i64;
        let scaled_height = (f64::from(self.source.h) * preshrink_ratio).floor() as i64;
        if preshrink_ratio < 1f64 {
            let mut vec = Vec::with_capacity(2);
            vec.push(s::DecoderCommand::JpegDownscaleHints(s::JpegIDCTDownscaleHints {
                scale_luma_spatially: Some(gamma_correct),
                gamma_correct_for_srgb_during_spatial_luma_scaling: Some(gamma_correct),
                width: scaled_width,
                height: scaled_height
            }));
            if !gamma_correct{
                vec.push(s::DecoderCommand::WebPDecoderHints(s::WebPDecoderHints{
                    width: scaled_width as i32,
                    height: scaled_height as i32,
                }));
            }
            Ok(Some(vec))
        } else {
            Ok(None)
        }
    }

    pub fn get_canvas_size(&self) -> sizing::Result<AspectRatio>{
        let i = self.i.parse()?.parsed;
        let (_, layout) = self.get_layout(&i).unwrap().get_crop_and_layout().unwrap();
        Ok(layout.get_box(BoxTarget::CurrentCanvas))
    }

    pub fn get_layout(&self, i: &Instructions) -> sizing::Result<Ir4Layout> {
        if i.trim_whitespace_threshold.is_some() {
            return Err(sizing::LayoutError::ContentDependent);
        }
        Ok(layout::Ir4Layout::new(*i, self.source.w, self.source.h))
    }

    pub fn expand_steps(&self) -> sizing::Result<Ir4Result> {
        let mut r = self.i.parse()?;

        let layout = self.get_layout(&r.parsed)?;

        let mut b = FramewiseBuilder::new();
        r.canvas = Some(layout.add_steps(&mut b)?.canvas);

        if let Some(n) = self.get_encoder_node(&r.parsed) {
            b.add(n);
        }
        r.steps = Some(b.into_steps());
        Ok(r)

    }

    pub fn get_encoder_node(&self, i: &Instructions) -> Option<s::Node>{

        if let Some(id) = self.encode_id {

            let format = i.format.or_else(|| self.source.get_format_from_mime())
                .unwrap_or_else(|| self.source.get_format_from_frame());

            let encoder = match format {
                OutputFormat::Gif => s::EncoderPreset::Gif,
                OutputFormat::Jpeg if Some(true) == i.jpeg_turbo => s::EncoderPreset::LibjpegTurbo {
                    quality: Some(i.quality.unwrap_or(90)),
                    progressive: i.jpeg_progressive,
                    optimize_huffman_coding:  i.jpeg_progressive
                },
                OutputFormat::Jpeg=> s::EncoderPreset::Mozjpeg {
                    quality: Some(i.quality.unwrap_or(90) as u8),
                    progressive: i.jpeg_progressive
                },
                OutputFormat::Png if i.png_quality.is_some() => s::EncoderPreset::Pngquant {
                    quality: Some((i.png_min_quality.unwrap_or(i.png_quality.unwrap()), i.png_quality.unwrap())),
                    speed: i.png_quantization_speed,
                    maximum_deflate: i.png_max_deflate
                },
                OutputFormat::Png if i.png_libpng == Some(true) => s::EncoderPreset::Libpng {
                    depth: Some(if i.bgcolor_srgb.is_some() { s::PngBitDepth::Png24 } else { s::PngBitDepth::Png32 }),
                    zlib_compression: if i.png_max_deflate == Some(true) { Some(9) } else { None },
                    matte: i.bgcolor_srgb.map(|sr| s::Color::Srgb(s::ColorSrgb::Hex(sr.to_rrggbbaa_string())))
                },
                OutputFormat::Png => s::EncoderPreset::Lodepng{
                    maximum_deflate: i.png_max_deflate
                },
                OutputFormat::Webp if i.webp_lossless == Some(true) => s::EncoderPreset::WebPLossless,
                OutputFormat::Webp => s::EncoderPreset::WebPLossy {
                    quality: i.webp_quality.unwrap_or(90f64) as f32
                },
            };
            Some(s::Node::Encode { io_id: id, preset: encoder })
        }else{
            None
        }
    }
}

<|endoftext|>