<commit_before>//! Utilities for handling the `wp_viewporter` protocol
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this implementation, create [`ViewporterState`], store it in your `State` struct and
//! implement the required traits, as shown in this example:
//!
//! ```
//! use smithay::wayland::viewporter::ViewporterState;
//! use smithay::delegate_viewporter;
//!
//! # struct State;
//! # let mut display = wayland_server::Display::<State>::new();
//!
//! // Create the viewporter state:
//! let viewporter_state = ViewporterState::new(
//!     &display.handle(), // the display
//!     None // provide a logger, if you want
//! );
//!
//! // implement Dispatch for the Viewporter types
//! delegate_viewporter!(State);
//!
//! // You're now ready to go!
//! ```
//!
//! ### Use the viewport state
//!
//! The [`viewport state`](ViewportCachedState) is double-buffered and
//! can be accessed by using the [`with_states`] function
//!
//! ```no_compile
//! let viewport = with_states(surface, |states| {
//!     states.cached_state.current::<ViewportCachedState>();
//! });
//! ```
//!
//! Before accessing the state you should call [`ensure_viewport_valid`]
//! to ensure the viewport is valid.
//!
//! Note: If you already hand over buffer management to smithay by using
//! [`on_commit_buffer_handler`](crate::backend::renderer::utils::on_commit_buffer_handler)
//! the implementation will already call [`ensure_viewport_valid`] for you.

use std::cell::RefCell;

use wayland_protocols::wp::viewporter::server::{wp_viewport, wp_viewporter};
use wayland_server::{
    backend::GlobalId, protocol::wl_surface, DelegateDispatch, DelegateGlobalDispatch, Dispatch,
    DisplayHandle, GlobalDispatch, Resource,
};

use crate::utils::{IsAlive, Logical, Rectangle, Size};

use super::compositor::{self, with_states, Cacheable, SurfaceData};

/// State of the wp_viewporter Global
#[derive(Debug)]
pub struct ViewporterState {
    dh: DisplayHandle,
    global: GlobalId,
}

impl Drop for ViewporterState {
    fn drop(&mut self) {
        self.dh.remove_global(self.global.clone())
    }
}

impl ViewporterState {
    /// Create new [`wp_viewporter`](wayland_protocols::viewporter::server::wp_viewporter) global.
    ///
    /// It returns the viewporter state, which you can drop to remove these global from
    /// the event loop in the future.
    pub fn new<D, L>(display: &DisplayHandle, log: L) -> ViewporterState
    where
        D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>
            + Dispatch<wp_viewporter::WpViewporter, slog::Logger>
            + Dispatch<wp_viewport::WpViewport, ViewportState>
            + 'static,
        L: Into<Option<slog::Logger>>,
    {
        ViewporterState {
            dh: display.clone(),
            global: display.create_global::<D, wp_viewporter::WpViewporter, slog::Logger>(
                1,
                crate::slog_or_fallback(log).new(slog::o!("smithay_module" => "wp_viewporter")),
            ),
        }
    }
}

impl<D> DelegateGlobalDispatch<wp_viewporter::WpViewporter, slog::Logger, D> for ViewporterState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn bind(
        _state: &mut D,
        _handle: &DisplayHandle,
        _client: &wayland_server::Client,
        resource: wayland_server::New<wp_viewporter::WpViewporter>,
        global_data: &slog::Logger,
        data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        data_init.init(resource, global_data.clone());
    }
}

impl<D> DelegateDispatch<wp_viewporter::WpViewporter, slog::Logger, D> for ViewporterState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        _resource: &wp_viewporter::WpViewporter,
        request: <wp_viewporter::WpViewporter as wayland_server::Resource>::Request,
        data: &slog::Logger,
        _dhandle: &DisplayHandle,
        data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            wp_viewporter::Request::GetViewport { id, surface } => {
                let already_has_viewport = with_states(&surface, |states| {
                    states
                        .data_map
                        .get::<RefCell<Option<ViewportMarker>>>()
                        .map(|v| v.borrow().is_some())
                        .unwrap_or(false)
                });

                if already_has_viewport {
                    surface.post_error(
                        wp_viewporter::Error::ViewportExists as u32,
                        "the surface already has a viewport object associated".to_string(),
                    );
                    return;
                }

                compositor::add_pre_commit_hook(&surface, viewport_commit_hook);

                let viewport = data_init.init(
                    id,
                    ViewportState {
                        surface: surface.clone(),
                        log: data.new(slog::o!("surface" => format!("{:?}", surface))),
                    },
                );
                with_states(&surface, |states| {
                    states
                        .data_map
                        .insert_if_missing(|| RefCell::new(Some(ViewportMarker(viewport))));
                })
            }
            wp_viewporter::Request::Destroy => {
                // All is already handled by our destructor
            }
            _ => unreachable!(),
        }
    }
}

impl<D> DelegateDispatch<wp_viewport::WpViewport, ViewportState, D> for ViewportState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        resource: &wp_viewport::WpViewport,
        request: <wp_viewport::WpViewport as wayland_server::Resource>::Request,
        data: &ViewportState,
        _dhandle: &DisplayHandle,
        _data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            wp_viewport::Request::Destroy => {
                let _ = with_states(&data.surface, |states| {
                    states
                        .data_map
                        .get::<RefCell<Option<ViewportMarker>>>()
                        .unwrap()
                        .borrow_mut()
                        .take();
                    *states.cached_state.pending::<ViewportCachedState>() = ViewportCachedState::default();
                });
            }
            wp_viewport::Request::SetSource { x, y, width, height } => {
                // If all of x, y, width and height are -1.0, the source rectangle is unset instead.
                // Any other set of values where width or height are zero or negative,
                // or x or y are negative, raise the bad_value protocol error.
                let is_unset = x == -1.0 && y == -1.0 && width == -1.0 && height == -1.0;
                let is_valid_src = x >= 0.0 && y >= 0.0 && width > 0.0 && height > 0.0;

                if !is_unset && !is_valid_src {
                    resource.post_error(
                        wp_viewport::Error::BadValue as u32,
                        "negative or zero values in width or height or negative values in x or y".to_string(),
                    );
                    return;
                }

                // If the wl_surface associated with the wp_viewport is destroyed,
                // all wp_viewport requests except 'destroy' raise the protocol error no_surface.
                if !data.surface.alive() {
                    resource.post_error(
                        wp_viewport::Error::NoSurface as u32,
                        "the wl_surface was destroyed".to_string(),
                    );
                    return;
                }

                with_states(&data.surface, |states| {
                    let mut viewport_state = states.cached_state.pending::<ViewportCachedState>();
                    let src = if is_unset {
                        None
                    } else {
                        let src = Rectangle::from_loc_and_size((x, y), (width, height));
                        slog::trace!(data.log, "New destination: {:?}", src);
                        Some(src)
                    };
                    viewport_state.src = src;
                });
            }
            wp_viewport::Request::SetDestination { width, height } => {
                // If width is -1 and height is -1, the destination size is unset instead.
                // Any other pair of values for width and height that contains zero or
                // negative values raises the bad_value protocol error.
                let is_unset = width == -1 && height == -1;
                let is_valid_size = width > 0 && height > 0;

                if !is_unset && !is_valid_size {
                    resource.post_error(
                        wp_viewport::Error::BadValue as u32,
                        "negative or zero values in width or height".to_string(),
                    );
                    return;
                }

                // If the wl_surface associated with the wp_viewport is destroyed,
                // all wp_viewport requests except 'destroy' raise the protocol error no_surface.
                if !data.surface.alive() {
                    resource.post_error(
                        wp_viewport::Error::NoSurface as u32,
                        "the wl_surface was destroyed".to_string(),
                    );
                }

                with_states(&data.surface, |states| {
                    let mut viewport_state = states.cached_state.pending::<ViewportCachedState>();
                    let size = if is_unset {
                        None
                    } else {
                        let dst = Size::from((width, height));
                        slog::trace!(data.log, "New destination: {:?}", dst);
                        Some(dst)
                    };
                    viewport_state.dst = size;
                });
            }
            _ => unreachable!(),
        }
    }
}

/// State of a single viewport attached to a surface
#[derive(Debug)]
pub struct ViewportState {
    surface: wl_surface::WlSurface,
    log: slog::Logger,
}

struct ViewportMarker(wp_viewport::WpViewport);

fn viewport_commit_hook(_dh: &DisplayHandle, surface: &wl_surface::WlSurface) {
    let _ = with_states(surface, |states| {
        states
            .data_map
            .insert_if_missing(|| RefCell::new(Option::<ViewportMarker>::None));
        let viewport = states
            .data_map
            .get::<RefCell<Option<ViewportMarker>>>()
            .unwrap()
            .borrow();
        if let Some(viewport) = &*viewport {
            let viewport_state = states.cached_state.pending::<ViewportCachedState>();

            // If src_width or src_height are not integers and destination size is not set,
            // the bad_size protocol error is raised when the surface state is applied.
            let src_size = viewport_state.src.map(|src| src.size);
            if viewport_state.dst.is_none()
                && src_size != src_size.map(|s| Size::from((s.w as i32, s.h as i32)).to_f64())
            {
                viewport.0.post_error(
                    wp_viewport::Error::BadSize as u32,
                    "destination size is not integer".to_string(),
                );
            }
        }
    });
}

/// Ensures that the viewport, if any, is valid accordingly to the protocol specification.
///
/// If the viewport violates any protocol checks a protocol error will be raised and `false`
/// is returned.
pub fn ensure_viewport_valid(states: &SurfaceData, buffer_size: Size<i32, Logical>) -> bool {
    states
        .data_map
        .insert_if_missing(|| RefCell::new(Option::<ViewportMarker>::None));
    let viewport = states
        .data_map
        .get::<RefCell<Option<ViewportMarker>>>()
        .unwrap()
        .borrow();

    if let Some(viewport) = &*viewport {
        let state = states.cached_state.pending::<ViewportCachedState>();

        let buffer_rect = Rectangle::from_loc_and_size((0.0, 0.0), buffer_size.to_f64());
        let src = state.src.unwrap_or(buffer_rect);
        let valid = buffer_rect.contains_rect(src);
        if !valid {
            viewport.0.post_error(
                wp_viewport::Error::OutOfBuffer as u32,
                "source rectangle extends outside of the content area".to_string(),
            );
        }
        valid
    } else {
        true
    }
}

/// Represents the double-buffered viewport
/// state of a [`WlSurface`](wl_surface::WlSurface)
#[derive(Debug, Default, Clone, Copy)]
pub struct ViewportCachedState {
    /// Defines the source [`Rectangle`] of the [`WlSurface`](wl_surface::WlSurface) in [`Logical`]
    /// coordinates used for cropping.
    pub src: Option<Rectangle<f64, Logical>>,
    /// Defines the destination [`Size`] of the [`WlSurface`](wl_surface::WlSurface) in [`Logical`]
    /// coordinates used for scaling.
    pub dst: Option<Size<i32, Logical>>,
}

impl ViewportCachedState {
    /// Gets the actual size the [`WlSurface`](wl_surface::WlSurface) should have on screen in
    /// [`Logical`] coordinates.
    ///
    /// This will return the destination size if set or the size of the source rectangle.
    /// If both are unset `None` is returned.
    pub fn size(&self) -> Option<Size<i32, Logical>> {
        self.dst.or_else(|| {
            self.src
                .map(|src| Size::from((src.size.w as i32, src.size.h as i32)))
        })
    }
}

impl Cacheable for ViewportCachedState {
    fn commit(&mut self, _dh: &DisplayHandle) -> Self {
        ViewportCachedState {
            src: self.src,
            dst: self.dst,
        }
    }

    fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
        into.src = self.src;
        into.dst = self.dst;
    }
}

#[allow(missing_docs)] // TODO
#[macro_export]
macro_rules! delegate_viewporter {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewporter::WpViewporter: slog::Logger
        ] => $crate::wayland::viewporter::ViewporterState);

        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewporter::WpViewporter: slog::Logger
        ] => $crate::wayland::viewporter::ViewporterState);
        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewport::WpViewport: $crate::wayland::viewporter::ViewportState
        ] => $crate::wayland::viewporter::ViewportState);
    };
}
<commit_msg>viewporter: fixup doc test<commit_after>//! Utilities for handling the `wp_viewporter` protocol
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize this implementation, create [`ViewporterState`], store it in your `State` struct and
//! implement the required traits, as shown in this example:
//!
//! ```
//! use smithay::wayland::viewporter::ViewporterState;
//! use smithay::delegate_viewporter;
//!
//! # struct State;
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//!
//! // Create the viewporter state:
//! let viewporter_state = ViewporterState::new(
//!     &display.handle(), // the display
//!     None // provide a logger, if you want
//! );
//!
//! // implement Dispatch for the Viewporter types
//! delegate_viewporter!(State);
//!
//! // You're now ready to go!
//! ```
//!
//! ### Use the viewport state
//!
//! The [`viewport state`](ViewportCachedState) is double-buffered and
//! can be accessed by using the [`with_states`] function
//!
//! ```no_compile
//! let viewport = with_states(surface, |states| {
//!     states.cached_state.current::<ViewportCachedState>();
//! });
//! ```
//!
//! Before accessing the state you should call [`ensure_viewport_valid`]
//! to ensure the viewport is valid.
//!
//! Note: If you already hand over buffer management to smithay by using
//! [`on_commit_buffer_handler`](crate::backend::renderer::utils::on_commit_buffer_handler)
//! the implementation will already call [`ensure_viewport_valid`] for you.

use std::cell::RefCell;

use wayland_protocols::wp::viewporter::server::{wp_viewport, wp_viewporter};
use wayland_server::{
    backend::GlobalId, protocol::wl_surface, DelegateDispatch, DelegateGlobalDispatch, Dispatch,
    DisplayHandle, GlobalDispatch, Resource,
};

use crate::utils::{IsAlive, Logical, Rectangle, Size};

use super::compositor::{self, with_states, Cacheable, SurfaceData};

/// State of the wp_viewporter Global
#[derive(Debug)]
pub struct ViewporterState {
    dh: DisplayHandle,
    global: GlobalId,
}

impl Drop for ViewporterState {
    fn drop(&mut self) {
        self.dh.remove_global(self.global.clone())
    }
}

impl ViewporterState {
    /// Create new [`wp_viewporter`](wayland_protocols::viewporter::server::wp_viewporter) global.
    ///
    /// It returns the viewporter state, which you can drop to remove these global from
    /// the event loop in the future.
    pub fn new<D, L>(display: &DisplayHandle, log: L) -> ViewporterState
    where
        D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>
            + Dispatch<wp_viewporter::WpViewporter, slog::Logger>
            + Dispatch<wp_viewport::WpViewport, ViewportState>
            + 'static,
        L: Into<Option<slog::Logger>>,
    {
        ViewporterState {
            dh: display.clone(),
            global: display.create_global::<D, wp_viewporter::WpViewporter, slog::Logger>(
                1,
                crate::slog_or_fallback(log).new(slog::o!("smithay_module" => "wp_viewporter")),
            ),
        }
    }
}

impl<D> DelegateGlobalDispatch<wp_viewporter::WpViewporter, slog::Logger, D> for ViewporterState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn bind(
        _state: &mut D,
        _handle: &DisplayHandle,
        _client: &wayland_server::Client,
        resource: wayland_server::New<wp_viewporter::WpViewporter>,
        global_data: &slog::Logger,
        data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        data_init.init(resource, global_data.clone());
    }
}

impl<D> DelegateDispatch<wp_viewporter::WpViewporter, slog::Logger, D> for ViewporterState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        _resource: &wp_viewporter::WpViewporter,
        request: <wp_viewporter::WpViewporter as wayland_server::Resource>::Request,
        data: &slog::Logger,
        _dhandle: &DisplayHandle,
        data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            wp_viewporter::Request::GetViewport { id, surface } => {
                let already_has_viewport = with_states(&surface, |states| {
                    states
                        .data_map
                        .get::<RefCell<Option<ViewportMarker>>>()
                        .map(|v| v.borrow().is_some())
                        .unwrap_or(false)
                });

                if already_has_viewport {
                    surface.post_error(
                        wp_viewporter::Error::ViewportExists as u32,
                        "the surface already has a viewport object associated".to_string(),
                    );
                    return;
                }

                compositor::add_pre_commit_hook(&surface, viewport_commit_hook);

                let viewport = data_init.init(
                    id,
                    ViewportState {
                        surface: surface.clone(),
                        log: data.new(slog::o!("surface" => format!("{:?}", surface))),
                    },
                );
                with_states(&surface, |states| {
                    states
                        .data_map
                        .insert_if_missing(|| RefCell::new(Some(ViewportMarker(viewport))));
                })
            }
            wp_viewporter::Request::Destroy => {
                // All is already handled by our destructor
            }
            _ => unreachable!(),
        }
    }
}

impl<D> DelegateDispatch<wp_viewport::WpViewport, ViewportState, D> for ViewportState
where
    D: GlobalDispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewporter::WpViewporter, slog::Logger>,
    D: Dispatch<wp_viewport::WpViewport, ViewportState>,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        resource: &wp_viewport::WpViewport,
        request: <wp_viewport::WpViewport as wayland_server::Resource>::Request,
        data: &ViewportState,
        _dhandle: &DisplayHandle,
        _data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
        match request {
            wp_viewport::Request::Destroy => {
                let _ = with_states(&data.surface, |states| {
                    states
                        .data_map
                        .get::<RefCell<Option<ViewportMarker>>>()
                        .unwrap()
                        .borrow_mut()
                        .take();
                    *states.cached_state.pending::<ViewportCachedState>() = ViewportCachedState::default();
                });
            }
            wp_viewport::Request::SetSource { x, y, width, height } => {
                // If all of x, y, width and height are -1.0, the source rectangle is unset instead.
                // Any other set of values where width or height are zero or negative,
                // or x or y are negative, raise the bad_value protocol error.
                let is_unset = x == -1.0 && y == -1.0 && width == -1.0 && height == -1.0;
                let is_valid_src = x >= 0.0 && y >= 0.0 && width > 0.0 && height > 0.0;

                if !is_unset && !is_valid_src {
                    resource.post_error(
                        wp_viewport::Error::BadValue as u32,
                        "negative or zero values in width or height or negative values in x or y".to_string(),
                    );
                    return;
                }

                // If the wl_surface associated with the wp_viewport is destroyed,
                // all wp_viewport requests except 'destroy' raise the protocol error no_surface.
                if !data.surface.alive() {
                    resource.post_error(
                        wp_viewport::Error::NoSurface as u32,
                        "the wl_surface was destroyed".to_string(),
                    );
                    return;
                }

                with_states(&data.surface, |states| {
                    let mut viewport_state = states.cached_state.pending::<ViewportCachedState>();
                    let src = if is_unset {
                        None
                    } else {
                        let src = Rectangle::from_loc_and_size((x, y), (width, height));
                        slog::trace!(data.log, "New destination: {:?}", src);
                        Some(src)
                    };
                    viewport_state.src = src;
                });
            }
            wp_viewport::Request::SetDestination { width, height } => {
                // If width is -1 and height is -1, the destination size is unset instead.
                // Any other pair of values for width and height that contains zero or
                // negative values raises the bad_value protocol error.
                let is_unset = width == -1 && height == -1;
                let is_valid_size = width > 0 && height > 0;

                if !is_unset && !is_valid_size {
                    resource.post_error(
                        wp_viewport::Error::BadValue as u32,
                        "negative or zero values in width or height".to_string(),
                    );
                    return;
                }

                // If the wl_surface associated with the wp_viewport is destroyed,
                // all wp_viewport requests except 'destroy' raise the protocol error no_surface.
                if !data.surface.alive() {
                    resource.post_error(
                        wp_viewport::Error::NoSurface as u32,
                        "the wl_surface was destroyed".to_string(),
                    );
                }

                with_states(&data.surface, |states| {
                    let mut viewport_state = states.cached_state.pending::<ViewportCachedState>();
                    let size = if is_unset {
                        None
                    } else {
                        let dst = Size::from((width, height));
                        slog::trace!(data.log, "New destination: {:?}", dst);
                        Some(dst)
                    };
                    viewport_state.dst = size;
                });
            }
            _ => unreachable!(),
        }
    }
}

/// State of a single viewport attached to a surface
#[derive(Debug)]
pub struct ViewportState {
    surface: wl_surface::WlSurface,
    log: slog::Logger,
}

struct ViewportMarker(wp_viewport::WpViewport);

fn viewport_commit_hook(_dh: &DisplayHandle, surface: &wl_surface::WlSurface) {
    let _ = with_states(surface, |states| {
        states
            .data_map
            .insert_if_missing(|| RefCell::new(Option::<ViewportMarker>::None));
        let viewport = states
            .data_map
            .get::<RefCell<Option<ViewportMarker>>>()
            .unwrap()
            .borrow();
        if let Some(viewport) = &*viewport {
            let viewport_state = states.cached_state.pending::<ViewportCachedState>();

            // If src_width or src_height are not integers and destination size is not set,
            // the bad_size protocol error is raised when the surface state is applied.
            let src_size = viewport_state.src.map(|src| src.size);
            if viewport_state.dst.is_none()
                && src_size != src_size.map(|s| Size::from((s.w as i32, s.h as i32)).to_f64())
            {
                viewport.0.post_error(
                    wp_viewport::Error::BadSize as u32,
                    "destination size is not integer".to_string(),
                );
            }
        }
    });
}

/// Ensures that the viewport, if any, is valid accordingly to the protocol specification.
///
/// If the viewport violates any protocol checks a protocol error will be raised and `false`
/// is returned.
pub fn ensure_viewport_valid(states: &SurfaceData, buffer_size: Size<i32, Logical>) -> bool {
    states
        .data_map
        .insert_if_missing(|| RefCell::new(Option::<ViewportMarker>::None));
    let viewport = states
        .data_map
        .get::<RefCell<Option<ViewportMarker>>>()
        .unwrap()
        .borrow();

    if let Some(viewport) = &*viewport {
        let state = states.cached_state.pending::<ViewportCachedState>();

        let buffer_rect = Rectangle::from_loc_and_size((0.0, 0.0), buffer_size.to_f64());
        let src = state.src.unwrap_or(buffer_rect);
        let valid = buffer_rect.contains_rect(src);
        if !valid {
            viewport.0.post_error(
                wp_viewport::Error::OutOfBuffer as u32,
                "source rectangle extends outside of the content area".to_string(),
            );
        }
        valid
    } else {
        true
    }
}

/// Represents the double-buffered viewport
/// state of a [`WlSurface`](wl_surface::WlSurface)
#[derive(Debug, Default, Clone, Copy)]
pub struct ViewportCachedState {
    /// Defines the source [`Rectangle`] of the [`WlSurface`](wl_surface::WlSurface) in [`Logical`]
    /// coordinates used for cropping.
    pub src: Option<Rectangle<f64, Logical>>,
    /// Defines the destination [`Size`] of the [`WlSurface`](wl_surface::WlSurface) in [`Logical`]
    /// coordinates used for scaling.
    pub dst: Option<Size<i32, Logical>>,
}

impl ViewportCachedState {
    /// Gets the actual size the [`WlSurface`](wl_surface::WlSurface) should have on screen in
    /// [`Logical`] coordinates.
    ///
    /// This will return the destination size if set or the size of the source rectangle.
    /// If both are unset `None` is returned.
    pub fn size(&self) -> Option<Size<i32, Logical>> {
        self.dst.or_else(|| {
            self.src
                .map(|src| Size::from((src.size.w as i32, src.size.h as i32)))
        })
    }
}

impl Cacheable for ViewportCachedState {
    fn commit(&mut self, _dh: &DisplayHandle) -> Self {
        ViewportCachedState {
            src: self.src,
            dst: self.dst,
        }
    }

    fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
        into.src = self.src;
        into.dst = self.dst;
    }
}

#[allow(missing_docs)] // TODO
#[macro_export]
macro_rules! delegate_viewporter {
    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
        $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewporter::WpViewporter: slog::Logger
        ] => $crate::wayland::viewporter::ViewporterState);

        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewporter::WpViewporter: slog::Logger
        ] => $crate::wayland::viewporter::ViewporterState);
        $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
            $crate::reexports::wayland_protocols::wp::viewporter::server::wp_viewport::WpViewport: $crate::wayland::viewporter::ViewportState
        ] => $crate::wayland::viewporter::ViewportState);
    };
}
<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::fs::File;
use std::io::{BufReader, Read};
use std::mem;
use std::path::Path;
use std::ptr;

use libsodium_sys;
use rustc_serialize::hex::ToHex;

use error::Result;

const BUF_SIZE: usize = 1024;

/// Calculate the BLAKE2b hash of a file, return as a hex string
/// digest size = 32 BYTES
/// NOTE: the hashing is keyless
pub fn hash_file<P: AsRef<Path>>(filename: &P) -> Result<String> {
    let file = try!(File::open(filename.as_ref()));
    let mut reader = BufReader::new(file);
    hash_reader(&mut reader)
}

pub fn hash_string(data: &str) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES];
    let mut st = vec![0u8; (unsafe { libsodium_sys::crypto_generichash_statebytes() })];
    let pst = unsafe {
        mem::transmute::<*mut u8, *mut libsodium_sys::crypto_generichash_state>(st.as_mut_ptr())
    };
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(out.to_hex())
}

pub fn hash_reader(reader: &mut BufReader<File>) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES];
    let mut st = vec![0u8; (unsafe { libsodium_sys::crypto_generichash_statebytes() })];
    let pst = unsafe {
        mem::transmute::<*mut u8, *mut libsodium_sys::crypto_generichash_state>(st.as_mut_ptr())
    };

    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
    }

    let mut buf = [0u8; BUF_SIZE];
    loop {
        let bytes_read = try!(reader.read(&mut buf));
        if bytes_read == 0 {
            break;
        }
        let chunk = &buf[0..bytes_read];
        unsafe {
            libsodium_sys::crypto_generichash_update(pst, chunk.as_ptr(), chunk.len() as u64);
        }
    }
    unsafe {
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(out.to_hex())
}

#[cfg(test)]
mod test {
    use std::env;
    #[allow(unused_imports)]
    use std::fs::{self, File};
    #[allow(unused_imports)]
    use std::io;
    use std::path::PathBuf;

    #[cfg(feature = "functional")]
    use hyper::{header, Client, Url};
    use super::*;
    use super::super::test_support::*;

    #[allow(dead_code)]
    fn mk_local_tmpdir() -> PathBuf {
        let dir = env::current_exe()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .join("tmp");
        fs::create_dir_all(&dir).unwrap();
        dir
    }

    #[test]
    fn hash_file_working() {
        // The expected values were computed using the `b2sum` program from
        // https://github.com/dchest/b2sum using the the `-s=32` option. For example:
        //      b2sum -s=32 signme.dat

        let computed = hash_file(&fixture("signme.dat")).unwrap();
        let expected = "20590a52c4f00588c500328b16d466c982a26fabaa5fa4dcc83052dd0a84f233";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.sig.key")).unwrap();
        let expected = "e966844bbc50b7a3a6d81e94dd38e27b92814b944095a8e55f1780921bfcfbe1";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.pub")).unwrap();
        let expected = "b80c4f412f9a0a7727b6e6f115e1b5fa3bae79ad2fcf47f769ed4e42cfb12265";
        assert_eq!(computed, expected);
    }

    #[test]
    #[cfg(feature = "functional")]
    fn hash_file_large_binary() {
        let url = "http://ftp.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.gz";
        let dst: PathBuf = {
            let file = mk_local_tmpdir().join(url.rsplit('/').next().unwrap());
            if !file.is_file() {
                let client = match env::var("http_proxy") {
                    Ok(url) => {
                        let url = Url::parse(&url).unwrap();
                        Client::with_http_proxy(url.host_str().unwrap().to_string(),
                                                url.port_or_known_default().unwrap())
                    }
                    _ => Client::new(),
                };
                let mut response = client.get(url)
                    .header(header::Connection::close())
                    .send()
                    .unwrap();
                let mut f = File::create(&file).unwrap();
                io::copy(&mut response, &mut f).unwrap();
            }
            file
        };
        let computed = hash_file(&dst).unwrap();
        let expected = "ba640dc063f0ed27e60b38dbb7cf19778cf7805d9fc91eb129fb68b409d46414";
        assert_eq!(computed, expected);
    }
}
<commit_msg>[rustfmt] Spacing fixes.<commit_after>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::fs::File;
use std::io::{BufReader, Read};
use std::mem;
use std::path::Path;
use std::ptr;

use libsodium_sys;
use rustc_serialize::hex::ToHex;

use error::Result;

const BUF_SIZE: usize = 1024;

/// Calculate the BLAKE2b hash of a file, return as a hex string
/// digest size = 32 BYTES
/// NOTE: the hashing is keyless
pub fn hash_file<P: AsRef<Path>>(filename: &P) -> Result<String> {
    let file = try!(File::open(filename.as_ref()));
    let mut reader = BufReader::new(file);
    hash_reader(&mut reader)
}

pub fn hash_string(data: &str) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES];
    let mut st = vec![0u8; (unsafe { libsodium_sys::crypto_generichash_statebytes() })];
    let pst = unsafe {
        mem::transmute::<*mut u8, *mut libsodium_sys::crypto_generichash_state>(st.as_mut_ptr())
    };
    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
        libsodium_sys::crypto_generichash_update(pst, data[..].as_ptr(), data.len() as u64);
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(out.to_hex())
}

pub fn hash_reader(reader: &mut BufReader<File>) -> Result<String> {
    let mut out = [0u8; libsodium_sys::crypto_generichash_BYTES];
    let mut st = vec![0u8; (unsafe { libsodium_sys::crypto_generichash_statebytes() })];
    let pst = unsafe {
        mem::transmute::<*mut u8, *mut libsodium_sys::crypto_generichash_state>(st.as_mut_ptr())
    };

    unsafe {
        libsodium_sys::crypto_generichash_init(pst, ptr::null_mut(), 0, out.len());
    }

    let mut buf = [0u8; BUF_SIZE];
    loop {
        let bytes_read = try!(reader.read(&mut buf));
        if bytes_read == 0 {
            break;
        }
        let chunk = &buf[0..bytes_read];
        unsafe {
            libsodium_sys::crypto_generichash_update(pst, chunk.as_ptr(), chunk.len() as u64);
        }
    }
    unsafe {
        libsodium_sys::crypto_generichash_final(pst, out.as_mut_ptr(), out.len());
    }
    Ok(out.to_hex())
}

#[cfg(test)]
mod test {
    use std::env;
    #[allow(unused_imports)]
    use std::fs::{self, File};
    #[allow(unused_imports)]
    use std::io;
    use std::path::PathBuf;

    #[cfg(feature = "functional")]
    use hyper::{header, Client, Url};
    use super::*;
    use super::super::test_support::*;

    #[allow(dead_code)]
    fn mk_local_tmpdir() -> PathBuf {
        let dir = env::current_exe()
                      .unwrap()
                      .parent()
                      .unwrap()
                      .parent()
                      .unwrap()
                      .parent()
                      .unwrap()
                      .parent()
                      .unwrap()
                      .parent()
                      .unwrap()
                      .join("tmp");
        fs::create_dir_all(&dir).unwrap();
        dir
    }

    #[test]
    fn hash_file_working() {
        // The expected values were computed using the `b2sum` program from
        // https://github.com/dchest/b2sum using the the `-s=32` option. For example:
        //      b2sum -s=32 signme.dat

        let computed = hash_file(&fixture("signme.dat")).unwrap();
        let expected = "20590a52c4f00588c500328b16d466c982a26fabaa5fa4dcc83052dd0a84f233";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.sig.key")).unwrap();
        let expected = "e966844bbc50b7a3a6d81e94dd38e27b92814b944095a8e55f1780921bfcfbe1";
        assert_eq!(computed, expected);

        let computed = hash_file(&fixture("happyhumans-20160424223347.pub")).unwrap();
        let expected = "b80c4f412f9a0a7727b6e6f115e1b5fa3bae79ad2fcf47f769ed4e42cfb12265";
        assert_eq!(computed, expected);
    }

    #[test]
    #[cfg(feature = "functional")]
    fn hash_file_large_binary() {
        let url = "http://ftp.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.gz";
        let dst: PathBuf = {
            let file = mk_local_tmpdir().join(url.rsplit('/').next().unwrap());
            if !file.is_file() {
                let client = match env::var("http_proxy") {
                    Ok(url) => {
                        let url = Url::parse(&url).unwrap();
                        Client::with_http_proxy(url.host_str().unwrap().to_string(),
                                                url.port_or_known_default().unwrap())
                    }
                    _ => Client::new(),
                };
                let mut response = client.get(url)
                                         .header(header::Connection::close())
                                         .send()
                                         .unwrap();
                let mut f = File::create(&file).unwrap();
                io::copy(&mut response, &mut f).unwrap();
            }
            file
        };
        let computed = hash_file(&dst).unwrap();
        let expected = "ba640dc063f0ed27e60b38dbb7cf19778cf7805d9fc91eb129fb68b409d46414";
        assert_eq!(computed, expected);
    }
}
<|endoftext|>


<commit_before>/// Entry in GDT or LDT. Provides size and location of a segment.
bitflags! {
    flags SegmentDescriptor: u64 {
        /// Descriptor type (0 = system; 1 = code or data).
        const DESCRIPTOR_S    = 1 << (31+12),
        /// Descriptor privilege level 0.
        const DESCRIPTOR_DPL0 = 0b00 << (31+13),
        /// Descriptor privilege level 1.
        const DESCRIPTOR_DPL1 = 0b01 << (31+13),
        /// Descriptor privilege level 2.
        const DESCRIPTOR_DPL2 = 0b10 << (31+13),
        /// Descriptor privilege level 3.
        const DESCRIPTOR_DPL3 = 0b11 << (31+13),
        /// Available for use by system software.
        const DESCRIPTOR_AVL  = 1 << (31+20),
        /// 64-bit code segment (IA-32e mode only).
        const DESCRIPTOR_L    = 1 << (31+21),
        /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment)
        const DESCRIPTOR_DB   = 1 << (31+22),
        ///  Granularity.
        const DESCRIPTOR_G    = 1 << (31+23),

        // System-Segment and Gate-Descriptor Types for IA32e mode.
        // When the S (descriptor type) flag in a segment descriptor is clear,
        // the descriptor type is a system descriptor.

        const TYPE_SYS_LDT = 0b0010 << (31+8),
        const TYPE_SYS_TSS_AVAILABLE = 0b1001 << (31+8),
        const TYPE_SYS_TSS_BUSY = 0b1011 << (31+8),
        const TYPE_SYS_CALL_GATE = 0b1100 << (31+8),
        const TYPE_SYS_INTERRUPT_GATE = 0b1110 << (31+8),
        const TYPE_SYS_TRAP_GATE = 0b1111 << (31+8),

        // Code- and Data-Segment Descriptor Types.
        // When the S (descriptor type) flag in a segment descriptor is set,
        // the descriptor is for either a code or a data segment.

        /// Data Read-Only
        const TYPE_D_RO = 0b0000 << (31+8),
        /// Data Read-Only, accessed
        const TYPE_D_ROA = 0b0001 << (31+8),
        /// Data Read/Write
        const TYPE_D_RW = 0b0010 << (31+8),
        /// Data Read/Write, accessed
        const TYPE_D_RWA = 0b0011 << (31+8),
        /// Data Read-Only, expand-down
        const TYPE_D_ROEXD = 0b0100 << (31+8),
        /// Data Read-Only, expand-down, accessed
        const TYPE_D_ROEXDA = 0b0101 << (31+8),
        /// Data Read/Write, expand-down
        const TYPE_D_RWEXD = 0b0110 << (31+8),
        /// Data Read/Write, expand-down, accessed
        const TYPE_D_RWEXDA = 0b0111 << (31+8),

        /// Code Execute-Only
        const TYPE_C_EO = 0b1000 << (31+8),
        /// Code Execute-Only, accessed
        const TYPE_C_EOA = 0b1001 << (31+8),
        /// Code Execute/Read
        const TYPE_C_ER = 0b1010 << (31+8),
        /// Code Execute/Read, accessed
        const TYPE_C_ERA = 0b1011 << (31+8),
        /// Code Execute-Only, conforming
        const TYPE_C_EOC = 0b1100 << (31+8),
        /// Code Execute-Only, conforming, accessed
        const TYPE_C_EOCA = 0b1101 << (31+8),
        /// Code Execute/Read, conforming
        const TYPE_C_ERC = 0b1110 << (31+8),
        /// Code Execute/Read, conforming, accessed
        const TYPE_C_ERCA = 0b1111 << (31+8),
    }
}

/// This is data-structure is a ugly mess thing so we provide some
/// convenience function to program it.
impl SegmentDescriptor {
    pub fn new(base: u32, limit: u32) -> SegmentDescriptor {
        let base_low: u64 = base as u64 & 0xffffff;
        let base_high: u64 = base as u64 & 0xff000000 >> 24;
        let limit_low: u64 = limit as u64 & 0xffff;
        let limit_high: u64 = (limit as u64 & (0b1111 << 16)) >> 16;

        SegmentDescriptor{
            bits: limit_low | base_low << 16 | limit_high << (31+16) | base_high << (31+24)
        }
    }
}

/// In 64-bit mode the TSS holds information that is not
/// directly related to the task-switch mechanism.
#[repr(C, packed)]
pub struct TaskStateSegement {
    reserved: u32,
    /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
    rsp: [u64; 3],
    reserved2: u64,
    /// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
    ist: [u64; 7],
    reserved3: u64,
    reserved4: u16,
    /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
    iomap_base: u16,
}

/// A struct describing a pointer to a descriptor table (GDT / IDT).
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
#[derive(Debug)]
#[repr(C, packed)]
pub struct DescriptorTablePointer {
   /// Size of the DT.
   pub limit: u16,
   /// Pointer to the memory region containing the DT.
   pub base: u64
}

/// Load GDT table.
pub unsafe fn lgdt(idt: &DescriptorTablePointer) {
    asm!("lgdt ($0)" :: "r" (idt));
}<commit_msg>Refactor flag names, add Debug trait.<commit_after>use core::fmt;

/// Entry in GDT or LDT. Provides size and location of a segment.
bitflags! {
    flags SegmentDescriptor: u64 {
        /// Descriptor type (0 = system; 1 = code or data).
        const DESC_S    = 1 << (31+12),
        /// Descriptor privilege level 0.
        const DESC_DPL0 = 0b00 << (31+13),
        /// Descriptor privilege level 1.
        const DESC_DPL1 = 0b01 << (31+13),
        /// Descriptor privilege level 2.
        const DESC_DPL2 = 0b10 << (31+13),
        /// Descriptor privilege level 3.
        const DESC_DPL3 = 0b11 << (31+13),
        /// Descriptor is Present.
        const DESC_P = 1 << (31+15),
        /// Available for use by system software.
        const DESC_AVL  = 1 << (31+20),
        /// 64-bit code segment (IA-32e mode only).
        const DESC_L    = 1 << (31+21),
        /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment)
        const DESC_DB   = 1 << (31+22),
        ///  Granularity.
        const DESC_G    = 1 << (31+23),

        // System-Segment and Gate-Descriptor Types for IA32e mode.
        // When the S (descriptor type) flag in a segment descriptor is clear,
        // the descriptor type is a system descriptor.

        const TYPE_SYS_LDT = 0b0010 << (31+8),
        const TYPE_SYS_TSS_AVAILABLE = 0b1001 << (31+8),
        const TYPE_SYS_TSS_BUSY = 0b1011 << (31+8),
        const TYPE_SYS_CALL_GATE = 0b1100 << (31+8),
        const TYPE_SYS_INTERRUPT_GATE = 0b1110 << (31+8),
        const TYPE_SYS_TRAP_GATE = 0b1111 << (31+8),

        // Code- and Data-Segment Descriptor Types.
        // When the S (descriptor type) flag in a segment descriptor is set,
        // the descriptor is for either a code or a data segment.

        /// Data Read-Only
        const TYPE_D_RO = 0b0000 << (31+8),
        /// Data Read-Only, accessed
        const TYPE_D_ROA = 0b0001 << (31+8),
        /// Data Read/Write
        const TYPE_D_RW = 0b0010 << (31+8),
        /// Data Read/Write, accessed
        const TYPE_D_RWA = 0b0011 << (31+8),
        /// Data Read-Only, expand-down
        const TYPE_D_ROEXD = 0b0100 << (31+8),
        /// Data Read-Only, expand-down, accessed
        const TYPE_D_ROEXDA = 0b0101 << (31+8),
        /// Data Read/Write, expand-down
        const TYPE_D_RWEXD = 0b0110 << (31+8),
        /// Data Read/Write, expand-down, accessed
        const TYPE_D_RWEXDA = 0b0111 << (31+8),

        /// Code Execute-Only
        const TYPE_C_EO = 0b1000 << (31+8),
        /// Code Execute-Only, accessed
        const TYPE_C_EOA = 0b1001 << (31+8),
        /// Code Execute/Read
        const TYPE_C_ER = 0b1010 << (31+8),
        /// Code Execute/Read, accessed
        const TYPE_C_ERA = 0b1011 << (31+8),
        /// Code Execute-Only, conforming
        const TYPE_C_EOC = 0b1100 << (31+8),
        /// Code Execute-Only, conforming, accessed
        const TYPE_C_EOCA = 0b1101 << (31+8),
        /// Code Execute/Read, conforming
        const TYPE_C_ERC = 0b1110 << (31+8),
        /// Code Execute/Read, conforming, accessed
        const TYPE_C_ERCA = 0b1111 << (31+8),
    }
}

/// This is data-structure is a ugly mess thing so we provide some
/// convenience function to program it.
impl SegmentDescriptor {
    pub fn new(base: u32, limit: u32) -> SegmentDescriptor {
        let base_low: u64 = base as u64 & 0xffffff;
        let base_high: u64 = base as u64 & 0xff000000 >> 24;
        let limit_low: u64 = limit as u64 & 0xffff;
        let limit_high: u64 = (limit as u64 & (0b1111 << 16)) >> 16;

        SegmentDescriptor{
            bits: limit_low | base_low << 16 | limit_high << (31+16) | base_high << (31+24)
        }
    }
}

impl fmt::Debug for SegmentDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SD: 0x{:x}", self.bits)
    }
}

/// In 64-bit mode the TSS holds information that is not
/// directly related to the task-switch mechanism.
#[repr(C, packed)]
pub struct TaskStateSegement {
    reserved: u32,
    /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
    rsp: [u64; 3],
    reserved2: u64,
    /// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
    ist: [u64; 7],
    reserved3: u64,
    reserved4: u16,
    /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
    iomap_base: u16,
}

/// A struct describing a pointer to a descriptor table (GDT / IDT).
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
#[derive(Debug)]
#[repr(C, packed)]
pub struct DescriptorTablePointer {
   /// Size of the DT.
   pub limit: u16,
   /// Pointer to the memory region containing the DT.
   pub base: u64
}

/// Load GDT table.
pub unsafe fn lgdt(idt: &DescriptorTablePointer) {
    asm!("lgdt ($0)" :: "r" (idt));
}<|endoftext|>


<commit_before>use super::*;
use sys::*;
/// A buffer large enough to hold an `SOLState` for diagnostics and a terminating zero.
pub type State = [SQLCHAR; SQL_SQLSTATE_SIZE + 1];

/// Result of `Diagnostics::diagnostics`
#[derive(Debug, Clone, Copy)]
pub struct DiagResult {
    /// A five-character SQLSTATE code (and terminating NULL) for the diagnostic record
    /// `rec_number`. The first two characters indicate the class; the next three indicate the
    /// subclass. For more information, see [SQLSTATE][1]s.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/sqlstates
    pub state: State,
    /// Native error code specific to the data source.
    pub native_error: SQLINTEGER,
    /// The total number of characters (excluding the terminating NULL) available to return in
    /// `message_text`.
    pub text_length: SQLSMALLINT,
}

// /// Returned by `Diagnostics::diagnostics`
// #[derive(Debug, Clone, Copy)]
// pub enum DiagReturn {
//     /// The function successfully returned diagnostic information.
//     Success(DiagResult),
//     /// The `message_text` buffer was too small to hold the requested diagnostic message. No
//     /// diagnostic records were generated. To determine that a truncation occurred, the application
//     /// must compare the buffer length to the actual number of bytes available, which is found in
//     /// `DiagResult::text_length`
//     Info(DiagResult),
//     /// `rec_number` was negative or `0`.
//     Error,
//     /// `rec_number` was greater than the number of diagnostic records that existed for the handle
//     /// specified in Handle. The function also returns `NoData` for any positive `rec_number` if
//     /// there are no diagnostic records available.
//     NoData,
// }

/// A type implementing this trait is able to provide diagnostic information regarding the last
/// method call.
pub trait Diagnostics {
    /// Returns the current values of multiple fields of a diagnostic record that contains error,
    /// warning, and status information.
    ///
    /// # Arguments
    ///
    /// * `rec_number` - Indicates the status record from which the application seeks information.
    ///                  Status records are numbered from 1.
    /// * `message_text` - Buffer in which to return the diagnostic message text string. If the
    ///                    number of characters to return is greater than the buffer length, the
    ///                    diagnostic message is truncated to `max(message_text.len() - 1, 0)`. For
    ///                    the format of the string, see [Diagnostic Messages][1]
    ///
    /// # Result
    ///`
    /// * `Success` - The function successfully returned diagnostic information.
    /// * `Info` - The `message_text` buffer was too small to hold the requested diagnostic message.
    ///            No diagnostic records were generated. To determine that a truncation occurred,
    ///            the application must compare the buffer length to the actual number of bytes
    ///            available, which is found in `DiagResult::text_length`
    /// * `Error` - `rec_number` was negative or `0`.
    /// * `NoData` - `rec_number` was greater than the number of diagnostic records that existed
    ///              for the specified Handle. The function also returns `NoData` for any positive
    ///              `rec_number` if there are no diagnostic records available.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/diagnostic-messages
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult>;
}

impl<H: Handle> Diagnostics for H {
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        unsafe {
            let mut text_length = 0;
            let mut state = [0; 6];
            let mut native_error = 0;
            let ret = SQLGetDiagRec(
                H::HANDLE_TYPE,
                self.handle(),
                rec_number,
                state.as_mut_ptr(),
                &mut native_error,
                message_text.as_mut_ptr(),
                message_text.buf_len(),
                &mut text_length,
            );
            let result = DiagResult {
                text_length: text_length,
                state: state,
                native_error: native_error,
            };
            match ret {
                SQL_SUCCESS => ReturnOption::Success(result),
                SQL_SUCCESS_WITH_INFO => ReturnOption::Info(result),
                SQL_ERROR => ReturnOption::Error(()),
                SQL_NO_DATA => ReturnOption::NoData(()),
                unexpected => panic!("SQLGetDiagRec returned: {:?}", unexpected),
            }
        }
    }
}

impl<S, E> Diagnostics for Return<S, E>
where
    S: Diagnostics,
    E: Diagnostics,
{
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        match *self {
            Success(ref s) | Info(ref s) => s.diagnostics(rec_number, message_text),
            Error(ref e) => e.diagnostics(rec_number, message_text),
        }
    }
}
<commit_msg>deleted code in comments<commit_after>use super::*;
use sys::*;
/// A buffer large enough to hold an `SOLState` for diagnostics and a terminating zero.
pub type State = [SQLCHAR; SQL_SQLSTATE_SIZE + 1];

/// Result of `Diagnostics::diagnostics`
#[derive(Debug, Clone, Copy)]
pub struct DiagResult {
    /// A five-character SQLSTATE code (and terminating NULL) for the diagnostic record
    /// `rec_number`. The first two characters indicate the class; the next three indicate the
    /// subclass. For more information, see [SQLSTATE][1]s.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/sqlstates
    pub state: State,
    /// Native error code specific to the data source.
    pub native_error: SQLINTEGER,
    /// The total number of characters (excluding the terminating NULL) available to return in
    /// `message_text`.
    pub text_length: SQLSMALLINT,
}

/// A type implementing this trait is able to provide diagnostic information regarding the last
/// method call.
pub trait Diagnostics {
    /// Returns the current values of multiple fields of a diagnostic record that contains error,
    /// warning, and status information.
    ///
    /// # Arguments
    ///
    /// * `rec_number` - Indicates the status record from which the application seeks information.
    ///                  Status records are numbered from 1.
    /// * `message_text` - Buffer in which to return the diagnostic message text string. If the
    ///                    number of characters to return is greater than the buffer length, the
    ///                    diagnostic message is truncated to `max(message_text.len() - 1, 0)`. For
    ///                    the format of the string, see [Diagnostic Messages][1]
    ///
    /// # Result
    ///`
    /// * `Success` - The function successfully returned diagnostic information.
    /// * `Info` - The `message_text` buffer was too small to hold the requested diagnostic message.
    ///            No diagnostic records were generated. To determine that a truncation occurred,
    ///            the application must compare the buffer length to the actual number of bytes
    ///            available, which is found in `DiagResult::text_length`
    /// * `Error` - `rec_number` was negative or `0`.
    /// * `NoData` - `rec_number` was greater than the number of diagnostic records that existed
    ///              for the specified Handle. The function also returns `NoData` for any positive
    ///              `rec_number` if there are no diagnostic records available.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/diagnostic-messages
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult>;
}

impl<H: Handle> Diagnostics for H {
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        unsafe {
            let mut text_length = 0;
            let mut state = [0; 6];
            let mut native_error = 0;
            let ret = SQLGetDiagRec(
                H::HANDLE_TYPE,
                self.handle(),
                rec_number,
                state.as_mut_ptr(),
                &mut native_error,
                message_text.as_mut_ptr(),
                message_text.buf_len(),
                &mut text_length,
            );
            let result = DiagResult {
                text_length: text_length,
                state: state,
                native_error: native_error,
            };
            match ret {
                SQL_SUCCESS => ReturnOption::Success(result),
                SQL_SUCCESS_WITH_INFO => ReturnOption::Info(result),
                SQL_ERROR => ReturnOption::Error(()),
                SQL_NO_DATA => ReturnOption::NoData(()),
                unexpected => panic!("SQLGetDiagRec returned: {:?}", unexpected),
            }
        }
    }
}

impl<S, E> Diagnostics for Return<S, E>
where
    S: Diagnostics,
    E: Diagnostics,
{
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        match *self {
            Success(ref s) | Info(ref s) => s.diagnostics(rec_number, message_text),
            Error(ref e) => e.diagnostics(rec_number, message_text),
        }
    }
}
<|endoftext|>


<commit_before>use libc;
use std::str::FromStr;
 use std::cell::RefCell;
use std::cmp::{self,Ord};
use std::sync::Mutex;
use std::fmt::{Arguments,format};
use std::io::{stdout,Stdout,Write};
use std::ascii::AsciiExt;
use std::net::{SocketAddr,UdpSocket};
use std::net::TcpStream;
use mio_uds::UnixDatagram;

thread_local! {
  pub static LOGGER: RefCell<Logger> = RefCell::new(Logger::new());
}

pub struct Logger {
  pub directives: Vec<LogDirective>,
  pub backend:    LoggerBackend,
  pub tag:        String,
  pub pid:        i32,
}

impl Logger {
  pub fn new() -> Logger {
    Logger {
      directives: vec!(LogDirective {
        name:  None,
        level: LogLevelFilter::Error,
      }),
      backend: LoggerBackend::Stdout(stdout()),
      tag:     "SOZU".to_string(),
      pid:     0,
    }
  }

  pub fn init(tag: String, spec: &str, backend: LoggerBackend) {
    let directives = parse_logging_spec(spec);
    LOGGER.with(|l| {
      let ref mut logger = *l.borrow_mut();
      logger.set_directives(directives);
      logger.backend = backend;
      logger.tag     = tag;
      logger.pid     = unsafe { libc::getpid() };
    });
  }

  pub fn log<'a>(&mut self, meta: &LogMetadata, args: Arguments) {
    if self.enabled(meta) {
      match self.backend {
        LoggerBackend::Stdout(ref mut stdout) => {
          stdout.write_fmt(args);
        },
        //FIXME: should have a buffer to write to instead of allocating a string
        LoggerBackend::Unix(ref mut socket) => {
          socket.send(format(args).as_bytes());
        },
        //FIXME: should have a buffer to write to instead of allocating a string
        LoggerBackend::Udp(ref mut socket, ref address) => {
          socket.send_to(format(args).as_bytes(), address);
        }
        LoggerBackend::Tcp(ref mut socket) => {
          socket.write_fmt(args);
        },
      }
    }
  }

  pub fn set_directives(&mut self, directives: Vec<LogDirective>) {
    self.directives = directives;
  }

  fn enabled(&self, meta: &LogMetadata) -> bool {
    // Search for the longest match, the vector is assumed to be pre-sorted.
    for directive in self.directives.iter().rev() {
      match directive.name {
        Some(ref name) if !meta.target.starts_with(&**name) => {},
        Some(..) | None => {
          return meta.level <= directive.level
        }
      }
    }
    false
  }
}

pub enum LoggerBackend {
  Stdout(Stdout),
  Unix(UnixDatagram),
  Udp(UdpSocket, SocketAddr),
  Tcp(TcpStream)
}

#[repr(usize)]
#[derive(Copy, Eq, Debug)]
pub enum LogLevel {
    /// The "error" level.
    ///
    /// Designates very serious errors.
    Error = 1, // This way these line up with the discriminants for LogLevelFilter below
    /// The "warn" level.
    ///
    /// Designates hazardous situations.
    Warn,
    /// The "info" level.
    ///
    /// Designates useful information.
    Info,
    /// The "debug" level.
    ///
    /// Designates lower priority information.
    Debug,
    /// The "trace" level.
    ///
    /// Designates very low priority, often extremely verbose, information.
    Trace,
}

static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO",
                                             "DEBUG", "TRACE"];

impl Clone for LogLevel {
    #[inline]
    fn clone(&self) -> LogLevel {
        *self
    }
}

impl PartialEq for LogLevel {
    #[inline]
    fn eq(&self, other: &LogLevel) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialEq<LogLevelFilter> for LogLevel {
    #[inline]
    fn eq(&self, other: &LogLevelFilter) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialOrd for LogLevel {
    #[inline]
    fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialOrd<LogLevelFilter> for LogLevel {
    #[inline]
    fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
        Some((*self as usize).cmp(&(*other as usize)))
    }
}

impl Ord for LogLevel {
    #[inline]
    fn cmp(&self, other: &LogLevel) -> cmp::Ordering {
        (*self as usize).cmp(&(*other as usize))
    }
}

impl LogLevel {
    fn from_usize(u: usize) -> Option<LogLevel> {
        match u {
            1 => Some(LogLevel::Error),
            2 => Some(LogLevel::Warn),
            3 => Some(LogLevel::Info),
            4 => Some(LogLevel::Debug),
            5 => Some(LogLevel::Trace),
            _ => None
        }
    }

    /// Returns the most verbose logging level.
    #[inline]
    pub fn max() -> LogLevel {
        LogLevel::Trace
    }

    /// Converts the `LogLevel` to the equivalent `LogLevelFilter`.
    #[inline]
    pub fn to_log_level_filter(&self) -> LogLevelFilter {
        LogLevelFilter::from_usize(*self as usize).unwrap()
    }
}

#[repr(usize)]
#[derive(Copy, Eq, Debug)]
pub enum LogLevelFilter {
    Off,
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl Clone for LogLevelFilter {
    #[inline]
    fn clone(&self) -> LogLevelFilter {
        *self
    }
}

impl PartialEq for LogLevelFilter {
    #[inline]
    fn eq(&self, other: &LogLevelFilter) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialEq<LogLevel> for LogLevelFilter {
    #[inline]
    fn eq(&self, other: &LogLevel) -> bool {
        other.eq(self)
    }
}

impl PartialOrd for LogLevelFilter {
    #[inline]
    fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialOrd<LogLevel> for LogLevelFilter {
    #[inline]
    fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
        other.partial_cmp(self).map(|x| x.reverse())
    }
}

impl Ord for LogLevelFilter {
    #[inline]
    fn cmp(&self, other: &LogLevelFilter) -> cmp::Ordering {
        (*self as usize).cmp(&(*other as usize))
    }
}

impl FromStr for LogLevelFilter {
    type Err = ();
    fn from_str(level: &str) -> Result<LogLevelFilter, ()> {
        ok_or(LOG_LEVEL_NAMES.iter()
                    .position(|&name| name.eq_ignore_ascii_case(level))
                    .map(|p| LogLevelFilter::from_usize(p).unwrap()), ())
    }
}

impl LogLevelFilter {
    fn from_usize(u: usize) -> Option<LogLevelFilter> {
        match u {
            0 => Some(LogLevelFilter::Off),
            1 => Some(LogLevelFilter::Error),
            2 => Some(LogLevelFilter::Warn),
            3 => Some(LogLevelFilter::Info),
            4 => Some(LogLevelFilter::Debug),
            5 => Some(LogLevelFilter::Trace),
            _ => None
        }
    }
    /// Returns the most verbose logging level filter.
    #[inline]
    pub fn max() -> LogLevelFilter {
        LogLevelFilter::Trace
    }

    /// Converts `self` to the equivalent `LogLevel`.
    ///
    /// Returns `None` if `self` is `LogLevelFilter::Off`.
    #[inline]
    pub fn to_log_level(&self) -> Option<LogLevel> {
        LogLevel::from_usize(*self as usize)
    }
}

/// Metadata about a log message.
pub struct LogMetadata {
  pub level:  LogLevel,
  pub target: &'static str,
}

pub struct LogDirective {
    name:  Option<String>,
    level: LogLevelFilter,
}

fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> {
    match t {
        Some(t) => Ok(t),
        None => Err(e),
    }
}

pub fn parse_logging_spec(spec: &str) -> Vec<LogDirective> {
    let mut dirs = Vec::new();

    let mut parts = spec.split('/');
    let mods = parts.next();
    let _    = parts.next();
    if parts.next().is_some() {
        println!("warning: invalid logging spec '{}', \
                 ignoring it (too many '/'s)", spec);
        return dirs;
    }
    mods.map(|m| { for s in m.split(',') {
        if s.len() == 0 { continue }
        let mut parts = s.split('=');
        let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
            (Some(part0), None, None) => {
                // if the single argument is a log-level string or number,
                // treat that as a global fallback
                match part0.parse() {
                    Ok(num) => (num, None),
                    Err(_) => (LogLevelFilter::max(), Some(part0)),
                }
            }
            (Some(part0), Some(""), None) => (LogLevelFilter::max(), Some(part0)),
            (Some(part0), Some(part1), None) => {
                match part1.parse() {
                    Ok(num) => (num, Some(part0)),
                    _ => {
                        println!("warning: invalid logging spec '{}', \
                                 ignoring it", part1);
                        continue
                    }
                }
            },
            _ => {
                println!("warning: invalid logging spec '{}', \
                         ignoring it", s);
                continue
            }
        };
        dirs.push(LogDirective {
            name: name.map(|s| s.to_string()),
            level: log_level,
        });
    }});

    return dirs;
}

#[macro_export]
macro_rules! log {
    (__inner__ $target:expr, $lvl:expr, $format:expr, $level_tag:expr,
     [$($transformed_args:ident),*], [$first_ident:ident $(, $other_idents:ident)*], $first_arg:expr $(, $other_args:expr)*) => ({
      let $first_ident = &$first_arg;
      log!(__inner__ $target, $lvl, $format, $level_tag, [$($transformed_args,)* $first_ident], [$($other_idents),*] $(, $other_args)*);
    });

    (__inner__ $target:expr, $lvl:expr, $format:expr, $level_tag:expr,
     [$($final_args:ident),*], [$($idents:ident),*]) => ({
      static _META: $crate::logging::LogMetadata = $crate::logging::LogMetadata {
          level:  $lvl,
          target: module_path!(),
      };
      {
        $crate::logging::LOGGER.with(|l| {
          l.borrow_mut().log(
            &_META,
            format_args!(
              concat!("{}\t{}\t{}\t{}\t{}\t", $format, '\n'),
              ::time::now_utc().rfc3339(), ::time::precise_time_ns(), l.borrow().pid,
              $level_tag, l.borrow().tag $(, $final_args)*)
          );
        });
      }
    });
    ($lvl:expr, $format:expr, $level_tag:expr $(, $args:expr)+) => {
      log!(__inner__ module_path!(), $lvl, $format, $level_tag, [], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v]
                  $(, $args)+)
    };
    ($lvl:expr, $format:expr, $level_tag:expr) => {
      log!(__inner__ module_path!(), $lvl, $format, $level_tag, [], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v])
    };
}

#[macro_export]
macro_rules! error {
    ($format:expr, $($arg:tt)*) => {
        log!($crate::logging::LogLevel::Error, $format, "ERROR", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Error, $format, "ERROR");
    };
}

#[macro_export]
macro_rules! warn {
    ($format:expr, $($arg:tt)*) => {
        use time;
        log!($crate::logging::LogLevel::Warn, $format, "WARN", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Warn, $format, "WARN");
    }
}

#[macro_export]
macro_rules! info {
    ($format:expr, $($arg:tt)*) => {
        log!($crate::logging::LogLevel::Info, $format, "INFO", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Info, $format, "INFO");
    }
}

#[macro_export]
macro_rules! debug {
    ($format:expr, $($arg:tt)*) => {
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Debug, concat!("{}\t", $format),
          "DEBUG", {module_path!()}, $($arg)*);
    };
    ($format:expr) => {
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Debug, concat!("{}\t", $format),
          "DEBUG", {module_path!()});
    }
}

#[macro_export]
macro_rules! trace {
    ($format:expr, $($arg:tt)*) => (
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Trace, concat!("{}\t", $format),
          "TRACE", module_path!(), $($arg)*);
    );
    ($format:expr) => (
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Trace, concat!("{}\t", $format),
          "TRACE", module_path!());
    )
}

#[macro_export]
macro_rules! setup_test_logger {
  () => (
    $crate::logging::Logger::init(module_path!().to_string(), "error", $crate::logging::LoggerBackend::Stdout(::std::io::stdout()));
  );
}
<commit_msg>fix runtime borrowing issues in logging<commit_after>use libc;
use std::str::FromStr;
 use std::cell::RefCell;
use std::cmp::{self,Ord};
use std::sync::Mutex;
use std::fmt::{Arguments,format};
use std::io::{stdout,Stdout,Write};
use std::ascii::AsciiExt;
use std::net::{SocketAddr,UdpSocket};
use std::net::TcpStream;
use mio_uds::UnixDatagram;

thread_local! {
  pub static LOGGER: RefCell<Logger> = RefCell::new(Logger::new());
  pub static TAG:    String          = LOGGER.with(|logger| (*logger.borrow()).tag.clone());
}

pub struct Logger {
  pub directives: Vec<LogDirective>,
  pub backend:    LoggerBackend,
  pub tag:        String,
  pub pid:        i32,
}

impl Logger {
  pub fn new() -> Logger {
    Logger {
      directives: vec!(LogDirective {
        name:  None,
        level: LogLevelFilter::Error,
      }),
      backend: LoggerBackend::Stdout(stdout()),
      tag:     "SOZU".to_string(),
      pid:     0,
    }
  }

  pub fn init(tag: String, spec: &str, backend: LoggerBackend) {
    let directives = parse_logging_spec(spec);
    LOGGER.with(|l| {
      let ref mut logger = *l.borrow_mut();
      logger.set_directives(directives);
      logger.backend = backend;
      logger.tag     = tag;
      logger.pid     = unsafe { libc::getpid() };
    });
  }

  pub fn log<'a>(&mut self, meta: &LogMetadata, args: Arguments) {
    if self.enabled(meta) {
      match self.backend {
        LoggerBackend::Stdout(ref mut stdout) => {
          stdout.write_fmt(args);
        },
        //FIXME: should have a buffer to write to instead of allocating a string
        LoggerBackend::Unix(ref mut socket) => {
          socket.send(format(args).as_bytes());
        },
        //FIXME: should have a buffer to write to instead of allocating a string
        LoggerBackend::Udp(ref mut socket, ref address) => {
          socket.send_to(format(args).as_bytes(), address);
        }
        LoggerBackend::Tcp(ref mut socket) => {
          socket.write_fmt(args);
        },
      }
    }
  }

  pub fn set_directives(&mut self, directives: Vec<LogDirective>) {
    self.directives = directives;
  }

  fn enabled(&self, meta: &LogMetadata) -> bool {
    // Search for the longest match, the vector is assumed to be pre-sorted.
    for directive in self.directives.iter().rev() {
      match directive.name {
        Some(ref name) if !meta.target.starts_with(&**name) => {},
        Some(..) | None => {
          return meta.level <= directive.level
        }
      }
    }
    false
  }
}

pub enum LoggerBackend {
  Stdout(Stdout),
  Unix(UnixDatagram),
  Udp(UdpSocket, SocketAddr),
  Tcp(TcpStream)
}

#[repr(usize)]
#[derive(Copy, Eq, Debug)]
pub enum LogLevel {
    /// The "error" level.
    ///
    /// Designates very serious errors.
    Error = 1, // This way these line up with the discriminants for LogLevelFilter below
    /// The "warn" level.
    ///
    /// Designates hazardous situations.
    Warn,
    /// The "info" level.
    ///
    /// Designates useful information.
    Info,
    /// The "debug" level.
    ///
    /// Designates lower priority information.
    Debug,
    /// The "trace" level.
    ///
    /// Designates very low priority, often extremely verbose, information.
    Trace,
}

static LOG_LEVEL_NAMES: [&'static str; 6] = ["OFF", "ERROR", "WARN", "INFO",
                                             "DEBUG", "TRACE"];

impl Clone for LogLevel {
    #[inline]
    fn clone(&self) -> LogLevel {
        *self
    }
}

impl PartialEq for LogLevel {
    #[inline]
    fn eq(&self, other: &LogLevel) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialEq<LogLevelFilter> for LogLevel {
    #[inline]
    fn eq(&self, other: &LogLevelFilter) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialOrd for LogLevel {
    #[inline]
    fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialOrd<LogLevelFilter> for LogLevel {
    #[inline]
    fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
        Some((*self as usize).cmp(&(*other as usize)))
    }
}

impl Ord for LogLevel {
    #[inline]
    fn cmp(&self, other: &LogLevel) -> cmp::Ordering {
        (*self as usize).cmp(&(*other as usize))
    }
}

impl LogLevel {
    fn from_usize(u: usize) -> Option<LogLevel> {
        match u {
            1 => Some(LogLevel::Error),
            2 => Some(LogLevel::Warn),
            3 => Some(LogLevel::Info),
            4 => Some(LogLevel::Debug),
            5 => Some(LogLevel::Trace),
            _ => None
        }
    }

    /// Returns the most verbose logging level.
    #[inline]
    pub fn max() -> LogLevel {
        LogLevel::Trace
    }

    /// Converts the `LogLevel` to the equivalent `LogLevelFilter`.
    #[inline]
    pub fn to_log_level_filter(&self) -> LogLevelFilter {
        LogLevelFilter::from_usize(*self as usize).unwrap()
    }
}

#[repr(usize)]
#[derive(Copy, Eq, Debug)]
pub enum LogLevelFilter {
    Off,
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl Clone for LogLevelFilter {
    #[inline]
    fn clone(&self) -> LogLevelFilter {
        *self
    }
}

impl PartialEq for LogLevelFilter {
    #[inline]
    fn eq(&self, other: &LogLevelFilter) -> bool {
        *self as usize == *other as usize
    }
}

impl PartialEq<LogLevel> for LogLevelFilter {
    #[inline]
    fn eq(&self, other: &LogLevel) -> bool {
        other.eq(self)
    }
}

impl PartialOrd for LogLevelFilter {
    #[inline]
    fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialOrd<LogLevel> for LogLevelFilter {
    #[inline]
    fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
        other.partial_cmp(self).map(|x| x.reverse())
    }
}

impl Ord for LogLevelFilter {
    #[inline]
    fn cmp(&self, other: &LogLevelFilter) -> cmp::Ordering {
        (*self as usize).cmp(&(*other as usize))
    }
}

impl FromStr for LogLevelFilter {
    type Err = ();
    fn from_str(level: &str) -> Result<LogLevelFilter, ()> {
        ok_or(LOG_LEVEL_NAMES.iter()
                    .position(|&name| name.eq_ignore_ascii_case(level))
                    .map(|p| LogLevelFilter::from_usize(p).unwrap()), ())
    }
}

impl LogLevelFilter {
    fn from_usize(u: usize) -> Option<LogLevelFilter> {
        match u {
            0 => Some(LogLevelFilter::Off),
            1 => Some(LogLevelFilter::Error),
            2 => Some(LogLevelFilter::Warn),
            3 => Some(LogLevelFilter::Info),
            4 => Some(LogLevelFilter::Debug),
            5 => Some(LogLevelFilter::Trace),
            _ => None
        }
    }
    /// Returns the most verbose logging level filter.
    #[inline]
    pub fn max() -> LogLevelFilter {
        LogLevelFilter::Trace
    }

    /// Converts `self` to the equivalent `LogLevel`.
    ///
    /// Returns `None` if `self` is `LogLevelFilter::Off`.
    #[inline]
    pub fn to_log_level(&self) -> Option<LogLevel> {
        LogLevel::from_usize(*self as usize)
    }
}

/// Metadata about a log message.
pub struct LogMetadata {
  pub level:  LogLevel,
  pub target: &'static str,
}

pub struct LogDirective {
    name:  Option<String>,
    level: LogLevelFilter,
}

fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> {
    match t {
        Some(t) => Ok(t),
        None => Err(e),
    }
}

pub fn parse_logging_spec(spec: &str) -> Vec<LogDirective> {
    let mut dirs = Vec::new();

    let mut parts = spec.split('/');
    let mods = parts.next();
    let _    = parts.next();
    if parts.next().is_some() {
        println!("warning: invalid logging spec '{}', \
                 ignoring it (too many '/'s)", spec);
        return dirs;
    }
    mods.map(|m| { for s in m.split(',') {
        if s.len() == 0 { continue }
        let mut parts = s.split('=');
        let (log_level, name) = match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
            (Some(part0), None, None) => {
                // if the single argument is a log-level string or number,
                // treat that as a global fallback
                match part0.parse() {
                    Ok(num) => (num, None),
                    Err(_) => (LogLevelFilter::max(), Some(part0)),
                }
            }
            (Some(part0), Some(""), None) => (LogLevelFilter::max(), Some(part0)),
            (Some(part0), Some(part1), None) => {
                match part1.parse() {
                    Ok(num) => (num, Some(part0)),
                    _ => {
                        println!("warning: invalid logging spec '{}', \
                                 ignoring it", part1);
                        continue
                    }
                }
            },
            _ => {
                println!("warning: invalid logging spec '{}', \
                         ignoring it", s);
                continue
            }
        };
        dirs.push(LogDirective {
            name: name.map(|s| s.to_string()),
            level: log_level,
        });
    }});

    return dirs;
}

#[macro_export]
macro_rules! log {
    (__inner__ $target:expr, $lvl:expr, $format:expr, $level_tag:expr,
     [$($transformed_args:ident),*], [$first_ident:ident $(, $other_idents:ident)*], $first_arg:expr $(, $other_args:expr)*) => ({
      let $first_ident = &$first_arg;
      log!(__inner__ $target, $lvl, $format, $level_tag, [$($transformed_args,)* $first_ident], [$($other_idents),*] $(, $other_args)*);
    });

    (__inner__ $target:expr, $lvl:expr, $format:expr, $level_tag:expr,
     [$($final_args:ident),*], [$($idents:ident),*]) => ({
      static _META: $crate::logging::LogMetadata = $crate::logging::LogMetadata {
          level:  $lvl,
          target: module_path!(),
      };
      {
        $crate::logging::TAG.with(|tag| {
          //let tag = t.borrow().tag;
          $crate::logging::LOGGER.with(|l| {
            let pid = l.borrow().pid;

            l.borrow_mut().log(
              &_META,
              format_args!(
                concat!("{}\t{}\t{}\t{}\t{}\t", $format, '\n'),
                ::time::now_utc().rfc3339(), ::time::precise_time_ns(), pid,
                $level_tag, tag $(, $final_args)*)
            );
          })
        });
      }
    });
    ($lvl:expr, $format:expr, $level_tag:expr $(, $args:expr)+) => {
      log!(__inner__ module_path!(), $lvl, $format, $level_tag, [], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v]
                  $(, $args)+)
    };
    ($lvl:expr, $format:expr, $level_tag:expr) => {
      log!(__inner__ module_path!(), $lvl, $format, $level_tag, [], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v])
    };
}

#[macro_export]
macro_rules! error {
    ($format:expr, $($arg:tt)*) => {
        log!($crate::logging::LogLevel::Error, $format, "ERROR", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Error, $format, "ERROR");
    };
}

#[macro_export]
macro_rules! warn {
    ($format:expr, $($arg:tt)*) => {
        use time;
        log!($crate::logging::LogLevel::Warn, $format, "WARN", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Warn, $format, "WARN");
    }
}

#[macro_export]
macro_rules! info {
    ($format:expr, $($arg:tt)*) => {
        log!($crate::logging::LogLevel::Info, $format, "INFO", $($arg)*);
    };
    ($format:expr) => {
        log!($crate::logging::LogLevel::Info, $format, "INFO");
    }
}

#[macro_export]
macro_rules! debug {
    ($format:expr, $($arg:tt)*) => {
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Debug, concat!("{}\t", $format),
          "DEBUG", {module_path!()}, $($arg)*);
    };
    ($format:expr) => {
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Debug, concat!("{}\t", $format),
          "DEBUG", {module_path!()});
    }
}

#[macro_export]
macro_rules! trace {
    ($format:expr, $($arg:tt)*) => (
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Trace, concat!("{}\t", $format),
          "TRACE", module_path!(), $($arg)*);
    );
    ($format:expr) => (
        #[cfg(debug_assertions)]
        log!($crate::logging::LogLevel::Trace, concat!("{}\t", $format),
          "TRACE", module_path!());
    )
}

#[macro_export]
macro_rules! setup_test_logger {
  () => (
    $crate::logging::Logger::init(module_path!().to_string(), "error", $crate::logging::LoggerBackend::Stdout(::std::io::stdout()));
  );
}
<|endoftext|>