<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().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);
    };
}
<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::<State, _>(
//!     &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>//! The `parser` module contains a lot of useful functionality
//! to parse tokens from the lexer (and creating the parsetree
//! and the ast)
//! its an predictiv parser for a LL(1) grammar
//! for more info about the parser: look in the Compiler Dragonbook,
//! Chapter 4.4.4, "Nonrecursive Predictive Parsing"

use frontend::lexer::Token;
use frontend::ast;
use frontend::parsetree::{PNode};
use self::NonTerminalType::*;
use frontend::lexer::Token::*;

pub fn parse_tokens(tokens: Vec<Token>) -> ast::AST {
    let mut parser: Parser = Parser::new(tokens);
    parser.parsing();
    parser.ast
}

//==============================
// grammar

#[derive(Debug, Copy, Clone)]
pub enum NonTerminalType {
    S,
    Sf,
    Passage,
    Passagef,
    PassageContent,
    Formating,
    BoldFormatting,
    ItalicFormatting,
    MonoFormatting,
    MonoContent,
    Link,
    Makro,
    Makrof,
    ExpressionList,
    ExpressionListf,
    Expression,
    E,
    E2,
    T,
    T2,
    B,
    B2,
    F,
    F2,
    G,
    G2,
    H,
    DataType,
    AssignVariable,
}

//==============================
// parser

struct Parser {
    ast: ast::AST,
    stack: Vec<PNode>,
    tokens: Vec<Token>,
    lookahead: usize
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Parser {
        Parser {
            ast: ast::AST::new(),
            stack: Vec::new(),
            tokens: tokens,
            lookahead: 0
        }
    }

    /// the predictive stack ll(1) parsing routine
    pub fn parsing(&mut self) {
        // push Start-Non-Terminal to the stack
        self.stack.push(PNode::new_non_terminal(S));

        while let Some(top) = self.stack.pop() {
            match top {
                PNode::NonTerminal(ref node) => {
                    self.apply_grammar(node.clone());
                }
                PNode::Terminal(_) => {
                    self.next_token();
                }
            }
        }
    }

    /// apply the ll(1) grammar
    /// the match-statement simulates the parsing-table behavior
    ///
    fn apply_grammar(&mut self, top: NonTerminalType) {
        if let Some(token) = self.tokens.get_mut(self.lookahead) {

            // the frst item in the tuple is the current state and
            // the snd is the current lookup-token
            let state_first: (NonTerminalType, &Token) = (top, token);

            let mut new_nodes = Vec::new();

            debug!("match {:?}", state_first);
            match state_first {
                (S, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Passage));
                    new_nodes.push(PNode::new_non_terminal(Sf));
                },
                (Sf, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(S));
                },
                (Passage, &TokPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageName(name.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_passage(TokPassageName(name.clone()));
                },

                // PassageContent
                (PassageContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (PassageContent, &TokFormatBoldStart) |
                (PassageContent, &TokFormatItalicStart) |
                (PassageContent, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(Formating));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokPassageLink(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Link));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokNewLine);
                },
                (PassageContent, &TokSet) |
                (PassageContent, &TokIf) |
                (PassageContent, &TokVariable(_)) |
                (PassageContent, &TokMakroVar(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Makro));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokEndIf) => {
                    // jump one ast-level higher
                    debug!("pop TokEndIf Passage;");

                    self.ast.up_child(TokEndIf, true);
                },
                (PassageContent, &TokFormatBoldEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, &TokFormatItalicEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, _) => {
                    // PassageContent -> ε
                },

                // Formating
                (Formating, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_non_terminal(BoldFormatting));
                },
                (Formating, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_non_terminal(ItalicFormatting));
                },
                (Formating, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(MonoFormatting));
                },

                // BoldFormatting
                (BoldFormatting, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatBoldStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatBoldEnd));

                    //ast
                    self.ast.child_down(TokFormatBoldStart);
                },

                // ItalicFormatting
                (ItalicFormatting, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatItalicStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatItalicEnd));

                    //ast
                    self.ast.child_down(TokFormatItalicStart);
                },

                // MonoFormatting
                (MonoFormatting, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatMonoStart));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                    new_nodes.push(PNode::new_terminal(TokFormatMonoEnd));

                    //ast
                    self.ast.child_down(TokFormatMonoStart);
                },

                // MonoContent
                (MonoContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                },
                (MonoContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                },

                // Link
                (Link, &TokPassageLink(ref text, ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageLink(text.clone(), name.clone())));

                    // ast
                    self.ast.add_child(TokPassageLink(text.clone(), name.clone()));
                },

                // Makro
                (Makro, &TokSet) => {
                    new_nodes.push(PNode::new_terminal(TokSet));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                },
                (Makro, &TokIf) => {
                    new_nodes.push(PNode::new_terminal(TokIf));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_non_terminal(Makrof));

                    // ast
                    self.ast.two_childs_down(TokIf, TokPseudo);
                },
                // means <<$var>>
                (Makro, &TokMakroVar(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroVar(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(TokMakroVar(name.clone()));
                },

                // Makrof
                (Makrof, &TokElse) => {
                    new_nodes.push(PNode::new_terminal(TokElse));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child_down(TokElse);
                },
                (Makrof, &TokEndIf) => {
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child(TokEndIf, false);
                }

                // ExpressionList
                (ExpressionList, &TokVariable(_)) |
                (ExpressionList, &TokInt(_)) |
                (ExpressionList, &TokString(_)) |
                (ExpressionList, &TokBoolean(_)) |
                (ExpressionList, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Expression));
                    new_nodes.push(PNode::new_non_terminal(ExpressionListf));
                },

                // ExpressionListf
                (ExpressionListf, &TokMakroEnd) => {
                    debug!("pop TokMakroEnd");
                    self.ast.up();

                },
                (ExpressionListf, _) => {
                    // ExpressionListf -> ε
                },

                // Expression
                (Expression, &TokVariable(_)) |
                (Expression, &TokInt(_)) |
                (Expression, &TokString(_)) |
                (Expression, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(E));
                },
                (Expression, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(AssignVariable));
                },

                // E
                (E, &TokVariable(_)) |
                (E, &TokInt(_)) |
                (E, &TokString(_)) |
                (E, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(T));
                    new_nodes.push(PNode::new_non_terminal(E2));
                },

                // E2
                (E2, _) => {
                    // E2 -> ε
                },

                // T
                (T, &TokVariable(_)) |
                (T, &TokInt(_)) |
                (T, &TokString(_)) |
                (T, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(B));
                    new_nodes.push(PNode::new_non_terminal(T2));
                },

                // T2
                (T2, _) => {
                    // T2 -> ε
                },

                // B
                (B, &TokVariable(_)) |
                (B, &TokInt(_)) |
                (B, &TokString(_)) |
                (B, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));
                },

                // B2
                (B2, &TokCompOp(ref op)) => {
                    new_nodes.push(PNode::new_terminal(TokCompOp(op.clone())));
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));

                    // ast
                    self.ast.add_child(TokCompOp(op.clone()));
                },
                (B2, _) => {
                    // B2 -> ε
                },

                // F
                (F, &TokVariable(_)) |
                (F, &TokInt(_)) |
                (F, &TokString(_)) |
                (F, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(G));
                    new_nodes.push(PNode::new_non_terminal(F2));
                },

                // F2
                (F2, _) => {
                    // F2 -> ε
                },

                // G
                (G, &TokVariable(_)) |
                (G, &TokInt(_)) |
                (G, &TokString(_)) |
                (G, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(H));
                    new_nodes.push(PNode::new_non_terminal(G2));
                },

                // G2
                (G2, _) => {
                    // G2 -> ε
                },

                // H
                (H, &TokInt(_)) |
                (H, &TokString(_)) |
                (H, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(DataType));
                },
                (H, &TokVariable(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokVariable(name.clone())));

                    // ast
                    self.ast.add_child(TokVariable(name.clone()));
                },

                // AssignVariable
                (AssignVariable, &TokAssign(ref name, ref assign)) => {
                    new_nodes.push(PNode::new_terminal(TokAssign(name.clone(), assign.clone())));
                    new_nodes.push(PNode::new_non_terminal(E));

                    //ast
                    self.ast.child_down(TokAssign(name.clone(), assign.clone()));
                },

                // DataType
                (DataType, &TokInt(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokInt(value.clone())));

                    // ast
                    self.ast.add_child(TokInt(value.clone()));
                },
                (DataType, &TokString(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokString(value.clone())));

                    // ast
                    self.ast.add_child(TokString(value.clone()));
                },
                (DataType, &TokBoolean(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokBoolean(value.clone())));

                    // ast
                    self.ast.add_child(TokBoolean(value.clone()));
                }

                _ => {
                    panic!("not supported grammar: {:?}", state_first);
                }
            }

            // adds the new nodes to the stack (in reversed order)
            while let Some(child) = new_nodes.pop() {
                self.stack.push(child);
            }

        } else {
            // no token left

            // Sf, PassageContent, Linkf,

            match top {
                Sf | PassageContent => {
                    // ... -> ε
                },
                _ => {
                    panic!("Nonterminal '{:?}' is not an allowed end.", top);
                }
            }
        }
    }

    /// sets the lookahead to the next token
    fn next_token(&mut self) {
        self.lookahead = self.lookahead + 1;
    }
}
<commit_msg>mono update<commit_after>//! The `parser` module contains a lot of useful functionality
//! to parse tokens from the lexer (and creating the parsetree
//! and the ast)
//! its an predictiv parser for a LL(1) grammar
//! for more info about the parser: look in the Compiler Dragonbook,
//! Chapter 4.4.4, "Nonrecursive Predictive Parsing"

use frontend::lexer::Token;
use frontend::ast;
use frontend::parsetree::{PNode};
use self::NonTerminalType::*;
use frontend::lexer::Token::*;

pub fn parse_tokens(tokens: Vec<Token>) -> ast::AST {
    let mut parser: Parser = Parser::new(tokens);
    parser.parsing();
    parser.ast
}

//==============================
// grammar

#[derive(Debug, Copy, Clone)]
pub enum NonTerminalType {
    S,
    Sf,
    Passage,
    Passagef,
    PassageContent,
    Formating,
    BoldFormatting,
    ItalicFormatting,
    MonoFormatting,
    MonoContent,
    Link,
    Makro,
    Makrof,
    ExpressionList,
    ExpressionListf,
    Expression,
    E,
    E2,
    T,
    T2,
    B,
    B2,
    F,
    F2,
    G,
    G2,
    H,
    DataType,
    AssignVariable,
}

//==============================
// parser

struct Parser {
    ast: ast::AST,
    stack: Vec<PNode>,
    tokens: Vec<Token>,
    lookahead: usize
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Parser {
        Parser {
            ast: ast::AST::new(),
            stack: Vec::new(),
            tokens: tokens,
            lookahead: 0
        }
    }

    /// the predictive stack ll(1) parsing routine
    pub fn parsing(&mut self) {
        // push Start-Non-Terminal to the stack
        self.stack.push(PNode::new_non_terminal(S));

        while let Some(top) = self.stack.pop() {
            match top {
                PNode::NonTerminal(ref node) => {
                    self.apply_grammar(node.clone());
                }
                PNode::Terminal(_) => {
                    self.next_token();
                }
            }
        }
    }

    /// apply the ll(1) grammar
    /// the match-statement simulates the parsing-table behavior
    ///
    fn apply_grammar(&mut self, top: NonTerminalType) {
        if let Some(token) = self.tokens.get_mut(self.lookahead) {

            // the frst item in the tuple is the current state and
            // the snd is the current lookup-token
            let state_first: (NonTerminalType, &Token) = (top, token);

            let mut new_nodes = Vec::new();

            debug!("match {:?}", state_first);
            match state_first {
                (S, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Passage));
                    new_nodes.push(PNode::new_non_terminal(Sf));
                },
                (Sf, &TokPassageName(_)) => {
                    new_nodes.push(PNode::new_non_terminal(S));
                },
                (Passage, &TokPassageName(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageName(name.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_passage(TokPassageName(name.clone()));
                },

                // PassageContent
                (PassageContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (PassageContent, &TokFormatBoldStart) |
                (PassageContent, &TokFormatItalicStart) |
                (PassageContent, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(Formating));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokPassageLink(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Link));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));

                    // ast
                    self.ast.add_child(TokNewLine);
                },
                (PassageContent, &TokSet) |
                (PassageContent, &TokIf) |
                (PassageContent, &TokVariable(_)) |
                (PassageContent, &TokMakroVar(_)) => {
                    new_nodes.push(PNode::new_non_terminal(Makro));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                },
                (PassageContent, &TokEndIf) => {
                    // jump one ast-level higher
                    debug!("pop TokEndIf Passage;");

                    self.ast.up_child(TokEndIf, true);
                },
                (PassageContent, &TokFormatBoldEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, &TokFormatItalicEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },
                (PassageContent, _) => {
                    // PassageContent -> ε
                },

                // Formating
                (Formating, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_non_terminal(BoldFormatting));
                },
                (Formating, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_non_terminal(ItalicFormatting));
                },
                (Formating, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_non_terminal(MonoFormatting));
                },

                // BoldFormatting
                (BoldFormatting, &TokFormatBoldStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatBoldStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatBoldEnd));

                    //ast
                    self.ast.child_down(TokFormatBoldStart);
                },

                // ItalicFormatting
                (ItalicFormatting, &TokFormatItalicStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatItalicStart));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokFormatItalicEnd));

                    //ast
                    self.ast.child_down(TokFormatItalicStart);
                },

                // MonoFormatting
                (MonoFormatting, &TokFormatMonoStart) => {
                    new_nodes.push(PNode::new_terminal(TokFormatMonoStart));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                    new_nodes.push(PNode::new_terminal(TokFormatMonoEnd));

                    //ast
                    self.ast.child_down(TokFormatMonoStart);
                },

                // MonoContent
                (MonoContent, &TokText(ref text)) => {
                    new_nodes.push(PNode::new_terminal(TokText(text.clone())));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));

                    // ast
                    self.ast.add_child(TokText(text.clone()));
                },
                (MonoContent, &TokNewLine) => {
                    new_nodes.push(PNode::new_terminal(TokNewLine));
                    new_nodes.push(PNode::new_non_terminal(MonoContent));
                },

                (MonoContent, &TokFormatMonoEnd) => {
                    // jump one ast-level higher
                    self.ast.up();
                },

                // Link
                (Link, &TokPassageLink(ref text, ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokPassageLink(text.clone(), name.clone())));

                    // ast
                    self.ast.add_child(TokPassageLink(text.clone(), name.clone()));
                },

                // Makro
                (Makro, &TokSet) => {
                    new_nodes.push(PNode::new_terminal(TokSet));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                },
                (Makro, &TokIf) => {
                    new_nodes.push(PNode::new_terminal(TokIf));
                    new_nodes.push(PNode::new_non_terminal(ExpressionList));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_non_terminal(Makrof));

                    // ast
                    self.ast.two_childs_down(TokIf, TokPseudo);
                },
                // means <<$var>>
                (Makro, &TokMakroVar(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokMakroVar(name.clone())));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.add_child(TokMakroVar(name.clone()));
                },

                // Makrof
                (Makrof, &TokElse) => {
                    new_nodes.push(PNode::new_terminal(TokElse));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));
                    new_nodes.push(PNode::new_non_terminal(PassageContent));
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child_down(TokElse);
                },
                (Makrof, &TokEndIf) => {
                    new_nodes.push(PNode::new_terminal(TokEndIf));
                    new_nodes.push(PNode::new_terminal(TokMakroEnd));

                    // ast
                    self.ast.up_child(TokEndIf, false);
                }

                // ExpressionList
                (ExpressionList, &TokVariable(_)) |
                (ExpressionList, &TokInt(_)) |
                (ExpressionList, &TokString(_)) |
                (ExpressionList, &TokBoolean(_)) |
                (ExpressionList, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(Expression));
                    new_nodes.push(PNode::new_non_terminal(ExpressionListf));
                },

                // ExpressionListf
                (ExpressionListf, &TokMakroEnd) => {
                    debug!("pop TokMakroEnd");
                    self.ast.up();

                },
                (ExpressionListf, _) => {
                    // ExpressionListf -> ε
                },

                // Expression
                (Expression, &TokVariable(_)) |
                (Expression, &TokInt(_)) |
                (Expression, &TokString(_)) |
                (Expression, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(E));
                },
                (Expression, &TokAssign(_, _)) => {
                    new_nodes.push(PNode::new_non_terminal(AssignVariable));
                },

                // E
                (E, &TokVariable(_)) |
                (E, &TokInt(_)) |
                (E, &TokString(_)) |
                (E, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(T));
                    new_nodes.push(PNode::new_non_terminal(E2));
                },

                // E2
                (E2, _) => {
                    // E2 -> ε
                },

                // T
                (T, &TokVariable(_)) |
                (T, &TokInt(_)) |
                (T, &TokString(_)) |
                (T, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(B));
                    new_nodes.push(PNode::new_non_terminal(T2));
                },

                // T2
                (T2, _) => {
                    // T2 -> ε
                },

                // B
                (B, &TokVariable(_)) |
                (B, &TokInt(_)) |
                (B, &TokString(_)) |
                (B, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));
                },

                // B2
                (B2, &TokCompOp(ref op)) => {
                    new_nodes.push(PNode::new_terminal(TokCompOp(op.clone())));
                    new_nodes.push(PNode::new_non_terminal(F));
                    new_nodes.push(PNode::new_non_terminal(B2));

                    // ast
                    self.ast.add_child(TokCompOp(op.clone()));
                },
                (B2, _) => {
                    // B2 -> ε
                },

                // F
                (F, &TokVariable(_)) |
                (F, &TokInt(_)) |
                (F, &TokString(_)) |
                (F, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(G));
                    new_nodes.push(PNode::new_non_terminal(F2));
                },

                // F2
                (F2, _) => {
                    // F2 -> ε
                },

                // G
                (G, &TokVariable(_)) |
                (G, &TokInt(_)) |
                (G, &TokString(_)) |
                (G, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(H));
                    new_nodes.push(PNode::new_non_terminal(G2));
                },

                // G2
                (G2, _) => {
                    // G2 -> ε
                },

                // H
                (H, &TokInt(_)) |
                (H, &TokString(_)) |
                (H, &TokBoolean(_)) => {
                    new_nodes.push(PNode::new_non_terminal(DataType));
                },
                (H, &TokVariable(ref name)) => {
                    new_nodes.push(PNode::new_terminal(TokVariable(name.clone())));

                    // ast
                    self.ast.add_child(TokVariable(name.clone()));
                },

                // AssignVariable
                (AssignVariable, &TokAssign(ref name, ref assign)) => {
                    new_nodes.push(PNode::new_terminal(TokAssign(name.clone(), assign.clone())));
                    new_nodes.push(PNode::new_non_terminal(E));

                    //ast
                    self.ast.child_down(TokAssign(name.clone(), assign.clone()));
                },

                // DataType
                (DataType, &TokInt(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokInt(value.clone())));

                    // ast
                    self.ast.add_child(TokInt(value.clone()));
                },
                (DataType, &TokString(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokString(value.clone())));

                    // ast
                    self.ast.add_child(TokString(value.clone()));
                },
                (DataType, &TokBoolean(ref value)) => {
                    new_nodes.push(PNode::new_terminal(TokBoolean(value.clone())));

                    // ast
                    self.ast.add_child(TokBoolean(value.clone()));
                }

                _ => {
                    panic!("not supported grammar: {:?}", state_first);
                }
            }

            // adds the new nodes to the stack (in reversed order)
            while let Some(child) = new_nodes.pop() {
                self.stack.push(child);
            }

        } else {
            // no token left

            // Sf, PassageContent, Linkf,

            match top {
                Sf | PassageContent => {
                    // ... -> ε
                },
                _ => {
                    panic!("Nonterminal '{:?}' is not an allowed end.", top);
                }
            }
        }
    }

    /// sets the lookahead to the next token
    fn next_token(&mut self) {
        self.lookahead = self.lookahead + 1;
    }
}
<|endoftext|>


<commit_before>use types::{Boolean, ShortString};

use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct AMQPFlags{
    flags: Vec<Boolean>,
    names: HashMap<ShortString, usize>,
}

impl AMQPFlags {
    pub fn new() -> AMQPFlags {
        Default::default()
    }

    pub fn add_flag(&mut self, name: ShortString, flag: Boolean) {
        // FIXME: handle collisions
        self.names.insert(name, self.flags.len());
        self.flags.push(flag);
    }

    pub fn get_flag(&self, name: &str) -> Option<Boolean> {
        if let Some(flag) = self.names.get(name).and_then(|index| self.flags.get(*index)) {
            Some(*flag)
        } else {
            None
        }
    }

    pub fn get_bytes(&self) -> Vec<u8> {
        self.flags.chunks(8).map(|v| {
            v.iter().enumerate().map(|(idx, b)| {
                if *b { 1 << idx } else { 0 }
            }).sum()
        }).collect()
    }

    pub fn from_bytes(names: &[&str], bytes: &[u8]) -> AMQPFlags {
        let flags : Vec<Boolean> = bytes.iter().flat_map(|b| {
            let mut v = Vec::new();
            for s in 0..8 {
                v.push(((b & (1 << s)) >> s) == 1)
            }
            v
        }).take(names.len()).collect();
        let len = flags.len();
        AMQPFlags {
            flags: flags,
            names: names.iter().take(len).enumerate().map(|(i, f)| (f.to_string(), i)).collect()
        }
    }
}

impl Default for AMQPFlags {
    fn default() -> AMQPFlags {
        AMQPFlags {
            flags: Vec::new(),
            names: HashMap::new(),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_empty_flags() {
        let empty: &[u8] = &[];
        assert_eq!(AMQPFlags::new().get_bytes().as_slice(), empty);
    }

    #[test]
    fn test_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        assert_eq!(flags.get_bytes().as_slice(), &[0b00011001])
    }

    #[test]
    fn test_many_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        flags.add_flag("f".to_string(), true);
        flags.add_flag("g".to_string(), false);
        flags.add_flag("h".to_string(), false);
        flags.add_flag("i".to_string(), true);
        flags.add_flag("j".to_string(), true);
        assert_eq!(flags.get_bytes().as_slice(), &[0b00111001, 0b00000011])
    }

    #[test]
    fn test_lookup_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        flags.add_flag("f".to_string(), true);
        flags.add_flag("g".to_string(), false);
        flags.add_flag("h".to_string(), false);
        flags.add_flag("i".to_string(), true);
        flags.add_flag("j".to_string(), true);
        assert_eq!(flags.get_flag("a"), Some(true));
        assert_eq!(flags.get_flag("d"), Some(true));
        assert_eq!(flags.get_flag("e"), Some(true));
        assert_eq!(flags.get_flag("b"), Some(false));
        assert_eq!(flags.get_flag("j"), Some(true));
        assert_eq!(flags.get_flag("h"), Some(false));
        assert_eq!(flags.get_flag("z"), None);
    }
}
<commit_msg>types: simplify flags<commit_after>use types::{Boolean, ShortString};

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct AMQPFlags{
    flags: Vec<(ShortString, Boolean)>,
}

impl AMQPFlags {
    pub fn new() -> AMQPFlags {
        Default::default()
    }

    pub fn add_flag(&mut self, name: ShortString, flag: Boolean) {
        self.flags.push((name, flag));
    }

    pub fn get_flag(&self, name: &str) -> Option<Boolean> {
        self.flags.iter().find(|(n, _)| n == name).map(|(_, v)| *v)
    }

    pub fn get_bytes(&self) -> Vec<u8> {
        self.flags.chunks(8).map(|v| {
            v.iter().enumerate().map(|(idx, (_, b))| {
                if *b { 1 << idx } else { 0 }
            }).sum()
        }).collect()
    }

    pub fn from_bytes(names: &[&str], bytes: &[u8]) -> AMQPFlags {
        let flags = names.iter().map(ToString::to_string).zip(bytes.iter().flat_map(|b| {
            let mut v = Vec::new();
            for s in 0..8 {
                v.push(((b & (1 << s)) >> s) == 1)
            }
            v
        })).collect();

        AMQPFlags {
            flags,
        }
    }
}

impl Default for AMQPFlags {
    fn default() -> AMQPFlags {
        AMQPFlags {
            flags: Vec::new(),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_empty_flags() {
        let empty: &[u8] = &[];
        assert_eq!(AMQPFlags::new().get_bytes().as_slice(), empty);
    }

    #[test]
    fn test_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        assert_eq!(flags.get_bytes().as_slice(), &[0b00011001])
    }

    #[test]
    fn test_many_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        flags.add_flag("f".to_string(), true);
        flags.add_flag("g".to_string(), false);
        flags.add_flag("h".to_string(), false);
        flags.add_flag("i".to_string(), true);
        flags.add_flag("j".to_string(), true);
        assert_eq!(flags.get_bytes().as_slice(), &[0b00111001, 0b00000011])
    }

    #[test]
    fn test_lookup_flags() {
        let mut flags = AMQPFlags::new();
        flags.add_flag("a".to_string(), true);
        flags.add_flag("b".to_string(), false);
        flags.add_flag("c".to_string(), false);
        flags.add_flag("d".to_string(), true);
        flags.add_flag("e".to_string(), true);
        flags.add_flag("f".to_string(), true);
        flags.add_flag("g".to_string(), false);
        flags.add_flag("h".to_string(), false);
        flags.add_flag("i".to_string(), true);
        flags.add_flag("j".to_string(), true);
        assert_eq!(flags.get_flag("a"), Some(true));
        assert_eq!(flags.get_flag("d"), Some(true));
        assert_eq!(flags.get_flag("e"), Some(true));
        assert_eq!(flags.get_flag("b"), Some(false));
        assert_eq!(flags.get_flag("j"), Some(true));
        assert_eq!(flags.get_flag("h"), Some(false));
        assert_eq!(flags.get_flag("z"), None);
    }
}
<|endoftext|>


<commit_before>// run-pass
// ignore-emscripten
// min-llvm-version 8.0

#![allow(non_camel_case_types)]
#![feature(repr_simd, platform_intrinsics)]

#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct u32x4(pub u32, pub u32, pub u32, pub u32);

#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct i32x4(pub i32, pub i32, pub i32, pub i32);

extern "platform-intrinsic" {
    fn simd_saturating_add<T>(x: T, y: T) -> T;
    fn simd_saturating_sub<T>(x: T, y: T) -> T;
}

fn main() {
    // unsigned
    {
        const M: u32 = u32::max_value();

        let a = u32x4(1, 2, 3, 4);
        let b = u32x4(2, 4, 6, 8);
        let m = u32x4(M, M, M, M);
        let m1 = u32x4(M - 1, M - 1, M - 1, M - 1);
        let z = u32x4(0, 0, 0, 0);

        unsafe {
            assert_eq!(simd_saturating_add(z, z), z);
            assert_eq!(simd_saturating_add(z, a), a);
            assert_eq!(simd_saturating_add(b, z), b);
            assert_eq!(simd_saturating_add(a, a), b);
            assert_eq!(simd_saturating_add(a, m), m);
            assert_eq!(simd_saturating_add(m, b), m);
            assert_eq!(simd_saturating_add(m1, a), m);

            assert_eq!(simd_saturating_sub(b, z), b);
            assert_eq!(simd_saturating_sub(b, a), a);
            assert_eq!(simd_saturating_sub(a, a), z);
            assert_eq!(simd_saturating_sub(a, b), z);
            assert_eq!(simd_saturating_sub(a, m1), z);
            assert_eq!(simd_saturating_sub(b, m1), z);
        }
    }

    // signed
    {
        const MIN: i32 = i32::min_value();
        const MAX: i32 = i32::max_value();

        let a = i32x4(1, 2, 3, 4);
        let b = i32x4(2, 4, 6, 8);
        let c = i32x4(-1, -2, -3, -4);
        let d = i32x4(-2, -4, -6, -8);

        let max = i32x4(MAX, MAX, MAX, MAX);
        let max1 = i32x4(MAX - 1, MAX - 1, MAX - 1, MAX - 1);
        let min = i32x4(MIN, MIN, MIN, MIN);
        let min1 = i32x4(MIN + 1, MIN + 1, MIN + 1, MIN + 1);

        let z = i32x4(0, 0, 0, 0);

        unsafe {
            assert_eq!(simd_saturating_add(z, z), z);
            assert_eq!(simd_saturating_add(z, a), a);
            assert_eq!(simd_saturating_add(b, z), b);
            assert_eq!(simd_saturating_add(a, a), b);
            assert_eq!(simd_saturating_add(a, max), max);
            assert_eq!(simd_saturating_add(max, b), max);
            assert_eq!(simd_saturating_add(max1, a), max);
            assert_eq!(simd_saturating_add(min1, z), min1);
            assert_eq!(simd_saturating_add(min, z), min);
            assert_eq!(simd_saturating_add(min1, c), min);
            assert_eq!(simd_saturating_add(min, c), min);
            assert_eq!(simd_saturating_add(min1, d), min);
            assert_eq!(simd_saturating_add(min, d), min);

            assert_eq!(simd_saturating_sub(b, z), b);
            assert_eq!(simd_saturating_sub(b, a), a);
            assert_eq!(simd_saturating_sub(a, a), z);
            assert_eq!(simd_saturating_sub(a, b), c);
            assert_eq!(simd_saturating_sub(z, max), min1);
            assert_eq!(simd_saturating_sub(min1, z), min1);
            assert_eq!(simd_saturating_sub(min1, a), min);
            assert_eq!(simd_saturating_sub(min1, b), min);
        }
    }
}
<commit_msg>Remove trailing white-space<commit_after>// run-pass
// ignore-emscripten
// min-llvm-version 8.0

#![allow(non_camel_case_types)]
#![feature(repr_simd, platform_intrinsics)]

#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct u32x4(pub u32, pub u32, pub u32, pub u32);

#[repr(simd)]
#[derive(Copy, Clone, PartialEq, Debug)]
struct i32x4(pub i32, pub i32, pub i32, pub i32);

extern "platform-intrinsic" {
    fn simd_saturating_add<T>(x: T, y: T) -> T;
    fn simd_saturating_sub<T>(x: T, y: T) -> T;
}

fn main() {
    // unsigned
    {
        const M: u32 = u32::max_value();

        let a = u32x4(1, 2, 3, 4);
        let b = u32x4(2, 4, 6, 8);
        let m = u32x4(M, M, M, M);
        let m1 = u32x4(M - 1, M - 1, M - 1, M - 1);
        let z = u32x4(0, 0, 0, 0);

        unsafe {
            assert_eq!(simd_saturating_add(z, z), z);
            assert_eq!(simd_saturating_add(z, a), a);
            assert_eq!(simd_saturating_add(b, z), b);
            assert_eq!(simd_saturating_add(a, a), b);
            assert_eq!(simd_saturating_add(a, m), m);
            assert_eq!(simd_saturating_add(m, b), m);
            assert_eq!(simd_saturating_add(m1, a), m);

            assert_eq!(simd_saturating_sub(b, z), b);
            assert_eq!(simd_saturating_sub(b, a), a);
            assert_eq!(simd_saturating_sub(a, a), z);
            assert_eq!(simd_saturating_sub(a, b), z);
            assert_eq!(simd_saturating_sub(a, m1), z);
            assert_eq!(simd_saturating_sub(b, m1), z);
        }
    }

    // signed
    {
        const MIN: i32 = i32::min_value();
        const MAX: i32 = i32::max_value();

        let a = i32x4(1, 2, 3, 4);
        let b = i32x4(2, 4, 6, 8);
        let c = i32x4(-1, -2, -3, -4);
        let d = i32x4(-2, -4, -6, -8);

        let max = i32x4(MAX, MAX, MAX, MAX);
        let max1 = i32x4(MAX - 1, MAX - 1, MAX - 1, MAX - 1);
        let min = i32x4(MIN, MIN, MIN, MIN);
        let min1 = i32x4(MIN + 1, MIN + 1, MIN + 1, MIN + 1);

        let z = i32x4(0, 0, 0, 0);

        unsafe {
            assert_eq!(simd_saturating_add(z, z), z);
            assert_eq!(simd_saturating_add(z, a), a);
            assert_eq!(simd_saturating_add(b, z), b);
            assert_eq!(simd_saturating_add(a, a), b);
            assert_eq!(simd_saturating_add(a, max), max);
            assert_eq!(simd_saturating_add(max, b), max);
            assert_eq!(simd_saturating_add(max1, a), max);
            assert_eq!(simd_saturating_add(min1, z), min1);
            assert_eq!(simd_saturating_add(min, z), min);
            assert_eq!(simd_saturating_add(min1, c), min);
            assert_eq!(simd_saturating_add(min, c), min);
            assert_eq!(simd_saturating_add(min1, d), min);
            assert_eq!(simd_saturating_add(min, d), min);

            assert_eq!(simd_saturating_sub(b, z), b);
            assert_eq!(simd_saturating_sub(b, a), a);
            assert_eq!(simd_saturating_sub(a, a), z);
            assert_eq!(simd_saturating_sub(a, b), c);
            assert_eq!(simd_saturating_sub(z, max), min1);
            assert_eq!(simd_saturating_sub(min1, z), min1);
            assert_eq!(simd_saturating_sub(min1, a), min);
            assert_eq!(simd_saturating_sub(min1, b), min);
        }
    }
}
<|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/. */

use dom::bindings::trace::JSTraceable;
use js::jsapi::{JSTracer};

use servo_util::task_state;
use servo_util::task_state::{SCRIPT, IN_GC};

use std::cell::{RefCell, Ref, RefMut};

/// A mutable field in the DOM.
///
/// This extends the API of `core::cell::RefCell` to allow unsafe access in
/// certain situations, with dynamic checking in debug builds.
pub struct DOMRefCell<T> {
    value: RefCell<T>,
}

// Functionality specific to Servo's `DOMRefCell` type
// ===================================================

impl<T> DOMRefCell<T> {
    /// Return a reference to the contents.
    ///
    /// For use in the layout task only.
    pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T {
        debug_assert!(task_state::get().is_layout());
        &*self.value.as_unsafe_cell().get()
    }

    /// Borrow the contents for the purpose of GC tracing.
    ///
    /// This succeeds even if the object is mutably borrowed,
    /// so you have to be careful in trace code!
    pub unsafe fn borrow_for_gc_trace<'a>(&'a self) -> &'a T {
        debug_assert!(task_state::get().contains(SCRIPT | IN_GC));
        &*self.value.as_unsafe_cell().get()
    }

    /// Is the cell mutably borrowed?
    ///
    /// For safety checks in debug builds only.
    pub fn is_mutably_borrowed(&self) -> bool {
        self.value.try_borrow().is_some()
    }

    pub fn try_borrow<'a>(&'a self) -> Option<Ref<'a, T>> {
        debug_assert!(task_state::get().is_script());
        self.value.try_borrow()
    }

    pub fn try_borrow_mut<'a>(&'a self) -> Option<RefMut<'a, T>> {
        debug_assert!(task_state::get().is_script());
        self.value.try_borrow_mut()
    }
}

impl<T: JSTraceable> JSTraceable for DOMRefCell<T> {
    fn trace(&self, trc: *mut JSTracer) {
        (*self).borrow().trace(trc)
    }
}

// Functionality duplicated with `core::cell::RefCell`
// ===================================================
impl<T> DOMRefCell<T> {
    pub fn new(value: T) -> DOMRefCell<T> {
        DOMRefCell {
            value: RefCell::new(value),
        }
    }

    pub fn unwrap(self) -> T {
        self.value.unwrap()
    }

    pub fn borrow<'a>(&'a self) -> Ref<'a, T> {
        match self.try_borrow() {
            Some(ptr) => ptr,
            None => panic!("DOMRefCell<T> already mutably borrowed")
        }
    }

    pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> {
        match self.try_borrow_mut() {
            Some(ptr) => ptr,
            None => panic!("DOMRefCell<T> already borrowed")
        }
    }
}
<commit_msg>Remove the unused DOMRefCell::unwrap method.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::trace::JSTraceable;
use js::jsapi::{JSTracer};

use servo_util::task_state;
use servo_util::task_state::{SCRIPT, IN_GC};

use std::cell::{RefCell, Ref, RefMut};

/// A mutable field in the DOM.
///
/// This extends the API of `core::cell::RefCell` to allow unsafe access in
/// certain situations, with dynamic checking in debug builds.
pub struct DOMRefCell<T> {
    value: RefCell<T>,
}

// Functionality specific to Servo's `DOMRefCell` type
// ===================================================

impl<T> DOMRefCell<T> {
    /// Return a reference to the contents.
    ///
    /// For use in the layout task only.
    pub unsafe fn borrow_for_layout<'a>(&'a self) -> &'a T {
        debug_assert!(task_state::get().is_layout());
        &*self.value.as_unsafe_cell().get()
    }

    /// Borrow the contents for the purpose of GC tracing.
    ///
    /// This succeeds even if the object is mutably borrowed,
    /// so you have to be careful in trace code!
    pub unsafe fn borrow_for_gc_trace<'a>(&'a self) -> &'a T {
        debug_assert!(task_state::get().contains(SCRIPT | IN_GC));
        &*self.value.as_unsafe_cell().get()
    }

    /// Is the cell mutably borrowed?
    ///
    /// For safety checks in debug builds only.
    pub fn is_mutably_borrowed(&self) -> bool {
        self.value.try_borrow().is_some()
    }

    pub fn try_borrow<'a>(&'a self) -> Option<Ref<'a, T>> {
        debug_assert!(task_state::get().is_script());
        self.value.try_borrow()
    }

    pub fn try_borrow_mut<'a>(&'a self) -> Option<RefMut<'a, T>> {
        debug_assert!(task_state::get().is_script());
        self.value.try_borrow_mut()
    }
}

impl<T: JSTraceable> JSTraceable for DOMRefCell<T> {
    fn trace(&self, trc: *mut JSTracer) {
        (*self).borrow().trace(trc)
    }
}

// Functionality duplicated with `core::cell::RefCell`
// ===================================================
impl<T> DOMRefCell<T> {
    pub fn new(value: T) -> DOMRefCell<T> {
        DOMRefCell {
            value: RefCell::new(value),
        }
    }

    pub fn borrow<'a>(&'a self) -> Ref<'a, T> {
        match self.try_borrow() {
            Some(ptr) => ptr,
            None => panic!("DOMRefCell<T> already mutably borrowed")
        }
    }

    pub fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> {
        match self.try_borrow_mut() {
            Some(ptr) => ptr,
            None => panic!("DOMRefCell<T> already borrowed")
        }
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn factor(rule: Rule) -> Rule {
    match rule {
        Rule { name, ty, expr } => Rule {
            name,
            ty,
            expr: expr.map_top_down(|expr| {
                // TODO: Use box syntax when it gets stabilized.
                match expr {
                    Expr::Choice(lhs, rhs) => match (*lhs, *rhs) {
                        (Expr::Seq(l1, r1), Expr::Seq(l2, r2)) => {
                            if l1 == l2 {
                                Expr::Seq(l1, Box::new(Expr::Choice(r1, r2)))
                            } else {
                                Expr::Choice(
                                    Box::new(Expr::Seq(l1, r1)),
                                    Box::new(Expr::Seq(l2, r2)),
                                )
                            }
                        }
                        (Expr::Seq(l1, l2), r) => {
                            if *l1 == r {
                                Expr::Seq(
                                    l1,
                                    Box::new(Expr::Opt(l2))
                                )
                            } else {
                                Expr::Choice(Box::new(Expr::Seq(l1, l2)), Box::new(r))
                            }
                        }
                        (lhs, rhs) => Expr::Choice(Box::new(lhs), Box::new(rhs)),
                    },
                    expr => expr,
                }
            }),
        },
    }
}
<commit_msg>Added case in factorizer when lhs is not Expr::Seq<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn factor(rule: Rule) -> Rule {
    match rule {
        Rule { name, ty, expr } => Rule {
            name,
            ty,
            expr: expr.map_top_down(|expr| {
                // TODO: Use box syntax when it gets stabilized.
                match expr {
                    Expr::Choice(lhs, rhs) => match (*lhs, *rhs) {
                        (Expr::Seq(l1, r1), Expr::Seq(l2, r2)) => {
                            if l1 == l2 {
                                Expr::Seq(l1, Box::new(Expr::Choice(r1, r2)))
                            } else {
                                Expr::Choice(
                                    Box::new(Expr::Seq(l1, r1)),
                                    Box::new(Expr::Seq(l2, r2)),
                                )
                            }
                        }
                        (Expr::Seq(l1, l2), r) => {
                            if *l1 == r {
                                Expr::Seq(
                                    l1,
                                    Box::new(Expr::Opt(l2))
                                )
                            } else {
                                Expr::Choice(Box::new(Expr::Seq(l1, l2)), Box::new(r))
                            }
                        }
                        (l, Expr::Seq(r1, r2)) => {
                            if l == *r1 {
                                l
                            } else {
                                Expr::Choice(Box::new(l), Box::new(Expr::Seq(r1, r2)))
                            }
                        }
                        (lhs, rhs) => Expr::Choice(Box::new(lhs), Box::new(rhs)),
                    },
                    expr => expr,
                }
            }),
        },
    }
}
<|endoftext|>