<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 https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLRenderingContext;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::webglframebuffer::WebGLFramebuffer;
use crate::dom::webglobject::WebGLObject;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl2renderingcontext::WebGL2RenderingContext;
use crate::dom::window::Window;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrsession::XRSession;
use crate::dom::xrview::XRView;
use crate::dom::xrviewport::XRViewport;
use canvas_traits::webgl::WebGLContextId;
use canvas_traits::webgl::WebGLCommand;
use canvas_traits::webgl::WebGLTextureId;
use dom_struct::dom_struct;
use euclid::{Rect, Size2D};
use std::convert::TryInto;
use webxr_api::ContextId as WebXRContextId;
use webxr_api::LayerId;
use webxr_api::LayerInit;
use webxr_api::Viewport;

#[derive(JSTraceable, MallocSizeOf)]
#[unrooted_must_root_lint::must_root]
pub enum RenderingContext {
    WebGL1(Dom<WebGLRenderingContext>),
    WebGL2(Dom<WebGL2RenderingContext>),
}

impl RenderingContext {
    fn context_id(&self) -> WebGLContextId {
        match self {
            RenderingContext::WebGL1(ref ctx) => ctx.context_id(),
            RenderingContext::WebGL2(ref ctx) => ctx.base_context().context_id(),
        }
    }
}

impl<'a> From<&'a XRWebGLLayerInit> for LayerInit {
    fn from(init: &'a XRWebGLLayerInit) -> LayerInit {
        LayerInit::WebGLLayer {
            alpha: init.alpha,
            antialias: init.antialias,
            depth: init.depth,
            stencil: init.stencil,
            framebuffer_scale_factor: *init.framebufferScaleFactor as f32,
            ignore_depth_values: init.ignoreDepthValues,
        }
    }
}

#[dom_struct]
pub struct XRWebGLLayer {
    reflector_: Reflector,
    antialias: bool,
    depth: bool,
    stencil: bool,
    alpha: bool,
    context: RenderingContext,
    session: Dom<XRSession>,
    /// If none, this is an inline session (the composition disabled flag is true)
    framebuffer: Option<Dom<WebGLFramebuffer>>,
    /// If none, this is an inline session (the composition disabled flag is true)
    #[ignore_malloc_size_of = "Layer ids don't heap-allocate"]
    layer_id: Option<LayerId>,
}

impl XRWebGLLayer {
    pub fn new_inherited(
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
        framebuffer: Option<&WebGLFramebuffer>,
        layer_id: Option<LayerId>,
    ) -> XRWebGLLayer {
        XRWebGLLayer {
            reflector_: Reflector::new(),
            antialias: init.antialias,
            depth: init.depth,
            stencil: init.stencil,
            alpha: init.alpha,
            layer_id,
            context: match context {
                XRWebGLRenderingContext::WebGLRenderingContext(ctx) => {
                    RenderingContext::WebGL1(Dom::from_ref(&*ctx))
                },
                XRWebGLRenderingContext::WebGL2RenderingContext(ctx) => {
                    RenderingContext::WebGL2(Dom::from_ref(&*ctx))
                },
            },
            session: Dom::from_ref(session),
            framebuffer: framebuffer.map(Dom::from_ref),
        }
    }

    pub fn new(
        global: &GlobalScope,
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
        framebuffer: Option<&WebGLFramebuffer>,
        layer_id: Option<LayerId>,
    ) -> DomRoot<XRWebGLLayer> {
        reflect_dom_object(
            Box::new(XRWebGLLayer::new_inherited(
                session,
                context,
                init,
                framebuffer,
                layer_id,
            )),
            global,
        )
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
    #[allow(non_snake_case)]
    pub fn Constructor(
        global: &Window,
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
    ) -> Fallible<DomRoot<Self>> {
        // Step 2
        if session.is_ended() {
            return Err(Error::InvalidState);
        }
        // XXXManishearth step 3: throw error if context is lost
        // XXXManishearth step 4: check XR compat flag for immersive sessions

        let (framebuffer, layer_id) = if session.is_immersive() {
            // Step 9.2. "Initialize layer’s framebuffer to a new opaque framebuffer created with context."
            let size = session
                .with_session(|session| session.recommended_framebuffer_resolution())
                .ok_or(Error::Operation)?;
            let framebuffer = WebGLFramebuffer::maybe_new_webxr(session, &context, size)
                .ok_or(Error::Operation)?;

            // Step 9.3. "Allocate and initialize resources compatible with session’s XR device,
            // including GPU accessible memory buffers, as required to support the compositing of layer."
            let context_id = WebXRContextId::from(context.context_id());
            let layer_init = LayerInit::from(init);
            let layer_id = session
                .with_session(|session| session.create_layer(context_id, layer_init))
                .map_err(|_| Error::Operation)?;

            // Step 9.4: "If layer’s resources were unable to be created for any reason,
            // throw an OperationError and abort these steps."
            (Some(framebuffer), Some(layer_id))
        } else {
            (None, None)
        };

        // Ensure that we finish setting up this layer before continuing.
        match context {
            XRWebGLRenderingContext::WebGLRenderingContext(ref ctx) => ctx.Finish(),
            XRWebGLRenderingContext::WebGL2RenderingContext(ref ctx) => ctx.Finish(),
        }

        // Step 10. "Return layer."
        Ok(XRWebGLLayer::new(
            &global.global(),
            session,
            context,
            init,
            framebuffer.as_deref(),
            layer_id,
        ))
    }

    pub fn layer_id(&self) -> Option<LayerId> {
        self.layer_id
    }

    pub fn context_id(&self) -> WebGLContextId {
        self.context.context_id()
    }

    pub fn session(&self) -> &XRSession {
        &self.session
    }

    pub fn size(&self) -> Size2D<u32, Viewport> {
        if let Some(framebuffer) = self.framebuffer.as_ref() {
            let size = framebuffer.size().unwrap_or((0, 0));
            Size2D::new(
                size.0.try_into().unwrap_or(0),
                size.1.try_into().unwrap_or(0),
            )
        } else {
            let size = match self.context {
                RenderingContext::WebGL1(ref ctx) => ctx.Canvas().get_size(),
                RenderingContext::WebGL2(ref ctx) => ctx.base_context().Canvas().get_size(),
            };
            Size2D::from_untyped(size)
        }
    }

    fn texture_target(&self) -> u32 {
        if cfg!(target_os = "macos") {
            sparkle::gl::TEXTURE_RECTANGLE
        } else {
            sparkle::gl::TEXTURE_2D
        }
    }

    pub fn begin_frame(&self, frame: &XRFrame) -> Option<()> {
        debug!("XRWebGLLayer begin frame");
        let framebuffer = self.framebuffer.as_ref()?;
        let context = framebuffer.upcast::<WebGLObject>().context();
        let sub_images = frame.get_sub_images(self.layer_id?)?;
        // TODO: Cache this texture
        let color_texture_id =
            WebGLTextureId::maybe_new(sub_images.sub_image.as_ref()?.color_texture)?;
        let color_texture = WebGLTexture::new(context, color_texture_id);
        let target = self.texture_target();
        // TODO: rebind the current bindings
        context.send_command(WebGLCommand::BindTexture(target, Some(color_texture_id)));
        framebuffer.bind(constants::FRAMEBUFFER);
        framebuffer
            .texture2d_even_if_opaque(
                constants::COLOR_ATTACHMENT0,
                self.texture_target(),
                Some(&color_texture),
                0,
            )
            .ok()?;
        if let Some(id) = sub_images.sub_image.as_ref()?.depth_stencil_texture {
            // TODO: Cache this texture
            let depth_stencil_texture_id = WebGLTextureId::maybe_new(id)?;
            let depth_stencil_texture = WebGLTexture::new(context, depth_stencil_texture_id);
            framebuffer
                .texture2d_even_if_opaque(
                    constants::DEPTH_STENCIL_ATTACHMENT,
                    constants::TEXTURE_2D,
                    Some(&depth_stencil_texture),
                    0,
                )
                .ok()?;
        }
        Some(())
    }

    pub fn end_frame(&self, _frame: &XRFrame) -> Option<()> {
        debug!("XRWebGLLayer end frame");
        // TODO: invalidate the old texture
        let framebuffer = self.framebuffer.as_ref()?;
        // TODO: rebind the current bindings
        framebuffer.bind(constants::FRAMEBUFFER);
        framebuffer
            .texture2d_even_if_opaque(constants::COLOR_ATTACHMENT0, self.texture_target(), None, 0)
            .ok()?;
        framebuffer
            .texture2d_even_if_opaque(
                constants::DEPTH_STENCIL_ATTACHMENT,
                constants::DEPTH_STENCIL_ATTACHMENT,
                None,
                0,
            )
            .ok()?;
        framebuffer.upcast::<WebGLObject>().context().Flush();
        Some(())
    }
}

impl XRWebGLLayerMethods for XRWebGLLayer {
    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-depth
    fn Depth(&self) -> bool {
        self.depth
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-stencil
    fn Stencil(&self) -> bool {
        self.stencil
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-antialias
    fn Antialias(&self) -> bool {
        self.antialias
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-alpha
    fn Alpha(&self) -> bool {
        self.alpha
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-context
    fn Context(&self) -> XRWebGLRenderingContext {
        match self.context {
            RenderingContext::WebGL1(ref ctx) => {
                XRWebGLRenderingContext::WebGLRenderingContext(DomRoot::from_ref(&**ctx))
            },
            RenderingContext::WebGL2(ref ctx) => {
                XRWebGLRenderingContext::WebGL2RenderingContext(DomRoot::from_ref(&**ctx))
            },
        }
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebuffer
    fn GetFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
        self.framebuffer.as_ref().map(|x| DomRoot::from_ref(&**x))
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth
    fn FramebufferWidth(&self) -> u32 {
        self.size().width
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight
    fn FramebufferHeight(&self) -> u32 {
        self.size().height
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
    fn GetViewport(&self, view: &XRView) -> Option<DomRoot<XRViewport>> {
        if self.session != view.session() {
            return None;
        }

        let index = view.viewport_index();

        let viewport = self.session.with_session(|s| {
            // Inline sssions
            if s.viewports().is_empty() {
                Rect::from_size(self.size().to_i32())
            } else {
                s.viewports()[index]
            }
        });

        Some(XRViewport::new(&self.global(), viewport))
    }
}
<commit_msg>Save / restore state when updating opaque framebuffer bindings<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 https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextConstants as constants;
use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::WebGL2RenderingContextBinding::WebGL2RenderingContextBinding::WebGL2RenderingContextMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerInit;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods;
use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLRenderingContext;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::webglframebuffer::WebGLFramebuffer;
use crate::dom::webglobject::WebGLObject;
use crate::dom::webgltexture::WebGLTexture;
use crate::dom::webglrenderingcontext::WebGLRenderingContext;
use crate::dom::webgl2renderingcontext::WebGL2RenderingContext;
use crate::dom::window::Window;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrsession::XRSession;
use crate::dom::xrview::XRView;
use crate::dom::xrviewport::XRViewport;
use canvas_traits::webgl::WebGLContextId;
use canvas_traits::webgl::WebGLCommand;
use canvas_traits::webgl::WebGLTextureId;
use dom_struct::dom_struct;
use euclid::{Rect, Size2D};
use std::convert::TryInto;
use webxr_api::ContextId as WebXRContextId;
use webxr_api::LayerId;
use webxr_api::LayerInit;
use webxr_api::Viewport;

#[derive(JSTraceable, MallocSizeOf)]
#[unrooted_must_root_lint::must_root]
pub enum RenderingContext {
    WebGL1(Dom<WebGLRenderingContext>),
    WebGL2(Dom<WebGL2RenderingContext>),
}

impl RenderingContext {
    fn context_id(&self) -> WebGLContextId {
        match self {
            RenderingContext::WebGL1(ref ctx) => ctx.context_id(),
            RenderingContext::WebGL2(ref ctx) => ctx.base_context().context_id(),
        }
    }
}

impl<'a> From<&'a XRWebGLLayerInit> for LayerInit {
    fn from(init: &'a XRWebGLLayerInit) -> LayerInit {
        LayerInit::WebGLLayer {
            alpha: init.alpha,
            antialias: init.antialias,
            depth: init.depth,
            stencil: init.stencil,
            framebuffer_scale_factor: *init.framebufferScaleFactor as f32,
            ignore_depth_values: init.ignoreDepthValues,
        }
    }
}

#[dom_struct]
pub struct XRWebGLLayer {
    reflector_: Reflector,
    antialias: bool,
    depth: bool,
    stencil: bool,
    alpha: bool,
    context: RenderingContext,
    session: Dom<XRSession>,
    /// If none, this is an inline session (the composition disabled flag is true)
    framebuffer: Option<Dom<WebGLFramebuffer>>,
    /// If none, this is an inline session (the composition disabled flag is true)
    #[ignore_malloc_size_of = "Layer ids don't heap-allocate"]
    layer_id: Option<LayerId>,
}

impl XRWebGLLayer {
    pub fn new_inherited(
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
        framebuffer: Option<&WebGLFramebuffer>,
        layer_id: Option<LayerId>,
    ) -> XRWebGLLayer {
        XRWebGLLayer {
            reflector_: Reflector::new(),
            antialias: init.antialias,
            depth: init.depth,
            stencil: init.stencil,
            alpha: init.alpha,
            layer_id,
            context: match context {
                XRWebGLRenderingContext::WebGLRenderingContext(ctx) => {
                    RenderingContext::WebGL1(Dom::from_ref(&*ctx))
                },
                XRWebGLRenderingContext::WebGL2RenderingContext(ctx) => {
                    RenderingContext::WebGL2(Dom::from_ref(&*ctx))
                },
            },
            session: Dom::from_ref(session),
            framebuffer: framebuffer.map(Dom::from_ref),
        }
    }

    pub fn new(
        global: &GlobalScope,
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
        framebuffer: Option<&WebGLFramebuffer>,
        layer_id: Option<LayerId>,
    ) -> DomRoot<XRWebGLLayer> {
        reflect_dom_object(
            Box::new(XRWebGLLayer::new_inherited(
                session,
                context,
                init,
                framebuffer,
                layer_id,
            )),
            global,
        )
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
    #[allow(non_snake_case)]
    pub fn Constructor(
        global: &Window,
        session: &XRSession,
        context: XRWebGLRenderingContext,
        init: &XRWebGLLayerInit,
    ) -> Fallible<DomRoot<Self>> {
        // Step 2
        if session.is_ended() {
            return Err(Error::InvalidState);
        }
        // XXXManishearth step 3: throw error if context is lost
        // XXXManishearth step 4: check XR compat flag for immersive sessions

        let (framebuffer, layer_id) = if session.is_immersive() {
            // Step 9.2. "Initialize layer’s framebuffer to a new opaque framebuffer created with context."
            let size = session
                .with_session(|session| session.recommended_framebuffer_resolution())
                .ok_or(Error::Operation)?;
            let framebuffer = WebGLFramebuffer::maybe_new_webxr(session, &context, size)
                .ok_or(Error::Operation)?;

            // Step 9.3. "Allocate and initialize resources compatible with session’s XR device,
            // including GPU accessible memory buffers, as required to support the compositing of layer."
            let context_id = WebXRContextId::from(context.context_id());
            let layer_init = LayerInit::from(init);
            let layer_id = session
                .with_session(|session| session.create_layer(context_id, layer_init))
                .map_err(|_| Error::Operation)?;

            // Step 9.4: "If layer’s resources were unable to be created for any reason,
            // throw an OperationError and abort these steps."
            (Some(framebuffer), Some(layer_id))
        } else {
            (None, None)
        };

        // Ensure that we finish setting up this layer before continuing.
        match context {
            XRWebGLRenderingContext::WebGLRenderingContext(ref ctx) => ctx.Finish(),
            XRWebGLRenderingContext::WebGL2RenderingContext(ref ctx) => ctx.Finish(),
        }

        // Step 10. "Return layer."
        Ok(XRWebGLLayer::new(
            &global.global(),
            session,
            context,
            init,
            framebuffer.as_deref(),
            layer_id,
        ))
    }

    pub fn layer_id(&self) -> Option<LayerId> {
        self.layer_id
    }

    pub fn context_id(&self) -> WebGLContextId {
        self.context.context_id()
    }

    pub fn session(&self) -> &XRSession {
        &self.session
    }

    pub fn size(&self) -> Size2D<u32, Viewport> {
        if let Some(framebuffer) = self.framebuffer.as_ref() {
            let size = framebuffer.size().unwrap_or((0, 0));
            Size2D::new(
                size.0.try_into().unwrap_or(0),
                size.1.try_into().unwrap_or(0),
            )
        } else {
            let size = match self.context {
                RenderingContext::WebGL1(ref ctx) => ctx.Canvas().get_size(),
                RenderingContext::WebGL2(ref ctx) => ctx.base_context().Canvas().get_size(),
            };
            Size2D::from_untyped(size)
        }
    }

    fn texture_target(&self) -> u32 {
        if cfg!(target_os = "macos") {
            sparkle::gl::TEXTURE_RECTANGLE
        } else {
            sparkle::gl::TEXTURE_2D
        }
    }

    pub fn begin_frame(&self, frame: &XRFrame) -> Option<()> {
        debug!("XRWebGLLayer begin frame");
        let framebuffer = self.framebuffer.as_ref()?;
        let context = framebuffer.upcast::<WebGLObject>().context();
        let sub_images = frame.get_sub_images(self.layer_id?)?;
        // TODO: Cache this texture
        let color_texture_id =
            WebGLTextureId::maybe_new(sub_images.sub_image.as_ref()?.color_texture)?;
        let color_texture = WebGLTexture::new(context, color_texture_id);
        let target = self.texture_target();

        // Save the current bindings
        let saved_framebuffer = context.get_draw_framebuffer_slot().get();
        let saved_framebuffer_target = framebuffer.target();
        let saved_texture_id = context
            .textures()
            .active_texture_slot(target, context.webgl_version())
            .ok()
            .and_then(|slot| slot.get().map(|texture| texture.id()));

        // We have to pick a framebuffer target.
        // If there is a draw framebuffer, we use its target,
        // otherwise we just use DRAW_FRAMEBUFFER.
        let framebuffer_target = saved_framebuffer
            .as_ref()
            .and_then(|fb| fb.target())
            .unwrap_or(constants::DRAW_FRAMEBUFFER);

        // Update the attachments
        context.send_command(WebGLCommand::BindTexture(target, Some(color_texture_id)));
        framebuffer.bind(framebuffer_target);
        framebuffer
            .texture2d_even_if_opaque(
                constants::COLOR_ATTACHMENT0,
                self.texture_target(),
                Some(&color_texture),
                0,
            )
            .ok()?;
        if let Some(id) = sub_images.sub_image.as_ref()?.depth_stencil_texture {
            // TODO: Cache this texture
            let depth_stencil_texture_id = WebGLTextureId::maybe_new(id)?;
            let depth_stencil_texture = WebGLTexture::new(context, depth_stencil_texture_id);
            framebuffer
                .texture2d_even_if_opaque(
                    constants::DEPTH_STENCIL_ATTACHMENT,
                    constants::TEXTURE_2D,
                    Some(&depth_stencil_texture),
                    0,
                )
                .ok()?;
        }

        // Restore the old bindings
        context.send_command(WebGLCommand::BindTexture(target, saved_texture_id));
        if let Some(framebuffer_target) = saved_framebuffer_target {
            framebuffer.bind(framebuffer_target);
        }
        if let Some(framebuffer) = saved_framebuffer {
            framebuffer.bind(framebuffer_target);
        }
        Some(())
    }

    pub fn end_frame(&self, _frame: &XRFrame) -> Option<()> {
        debug!("XRWebGLLayer end frame");
        // TODO: invalidate the old texture
        let framebuffer = self.framebuffer.as_ref()?;
        // TODO: rebind the current bindings
        framebuffer.bind(constants::FRAMEBUFFER);
        framebuffer
            .texture2d_even_if_opaque(constants::COLOR_ATTACHMENT0, self.texture_target(), None, 0)
            .ok()?;
        framebuffer
            .texture2d_even_if_opaque(
                constants::DEPTH_STENCIL_ATTACHMENT,
                constants::DEPTH_STENCIL_ATTACHMENT,
                None,
                0,
            )
            .ok()?;
        framebuffer.upcast::<WebGLObject>().context().Flush();
        Some(())
    }
}

impl XRWebGLLayerMethods for XRWebGLLayer {
    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-depth
    fn Depth(&self) -> bool {
        self.depth
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-stencil
    fn Stencil(&self) -> bool {
        self.stencil
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-antialias
    fn Antialias(&self) -> bool {
        self.antialias
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-alpha
    fn Alpha(&self) -> bool {
        self.alpha
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-context
    fn Context(&self) -> XRWebGLRenderingContext {
        match self.context {
            RenderingContext::WebGL1(ref ctx) => {
                XRWebGLRenderingContext::WebGLRenderingContext(DomRoot::from_ref(&**ctx))
            },
            RenderingContext::WebGL2(ref ctx) => {
                XRWebGLRenderingContext::WebGL2RenderingContext(DomRoot::from_ref(&**ctx))
            },
        }
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebuffer
    fn GetFramebuffer(&self) -> Option<DomRoot<WebGLFramebuffer>> {
        self.framebuffer.as_ref().map(|x| DomRoot::from_ref(&**x))
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferwidth
    fn FramebufferWidth(&self) -> u32 {
        self.size().width
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-framebufferheight
    fn FramebufferHeight(&self) -> u32 {
        self.size().height
    }

    /// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
    fn GetViewport(&self, view: &XRView) -> Option<DomRoot<XRViewport>> {
        if self.session != view.session() {
            return None;
        }

        let index = view.viewport_index();

        let viewport = self.session.with_session(|s| {
            // Inline sssions
            if s.viewports().is_empty() {
                Rect::from_size(self.size().to_i32())
            } else {
                s.viewports()[index]
            }
        });

        Some(XRViewport::new(&self.global(), viewport))
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    use crate::*;

    fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option<RawStrError>) {
        let s = &format!("r{}", s);
        let mut cursor = Cursor::new(s);
        cursor.bump();
        let (n_hashes, err) = cursor.raw_double_quoted_string(0);
        assert_eq!(n_hashes, expected_hashes);
        assert_eq!(err, expected_err);
    }

    #[test]
    fn test_naked_raw_str() {
        check_raw_str(r#""abc""#, 0, None);
    }

    #[test]
    fn test_raw_no_start() {
        check_raw_str(r##""abc"#"##, 0, None);
    }

    #[test]
    fn test_too_many_terminators() {
        // this error is handled in the parser later
        check_raw_str(r###"#"abc"##"###, 1, None);
    }

    #[test]
    fn test_unterminated() {
        check_raw_str(
            r#"#"abc"#,
            1,
            Some(RawStrError::NoTerminator {
                expected: 1,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
        check_raw_str(
            r###"##"abc"#"###,
            2,
            Some(RawStrError::NoTerminator {
                expected: 2,
                found: 1,
                possible_terminator_offset: Some(7),
            }),
        );
        // We're looking for "# not just any #
        check_raw_str(
            r###"##"abc#"###,
            2,
            Some(RawStrError::NoTerminator {
                expected: 2,
                found: 0,
                possible_terminator_offset: None,
            }),
        )
    }

    #[test]
    fn test_invalid_start() {
        check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' }));
    }

    #[test]
    fn test_unterminated_no_pound() {
        // https://github.com/rust-lang/rust/issues/70677
        check_raw_str(
            r#"""#,
            0,
            Some(RawStrError::NoTerminator {
                expected: 0,
                found: 0,
                possible_terminator_offset: None,
            }),
        );
    }

    #[test]
    fn test_valid_shebang() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!/usr/bin/rustrun\nlet x = 5;";
        assert_eq!(strip_shebang(input), Some(18));
    }

    #[test]
    fn test_invalid_shebang_valid_rust_syntax() {
        // https://github.com/rust-lang/rust/issues/70528
        let input = "#!    [bad_attribute]";
        assert_eq!(strip_shebang(input), None);
    }

    #[test]
    fn test_shebang_second_line() {
        // Because shebangs are interpreted by the kernel, they must be on the first line
        let input = "\n#!/bin/bash";
        assert_eq!(strip_shebang(input), None);
    }

    #[test]
    fn test_shebang_space() {
        let input = "#!    /bin/bash";
        assert_eq!(strip_shebang(input), Some(input.len()));
    }

    #[test]
    fn test_shebang_empty_shebang() {
        let input = "#!    \n[attribute(foo)]";
        assert_eq!(strip_shebang(input), None);
    }

    #[test]
    fn test_invalid_shebang_comment() {
        let input = "#!//bin/ami/a/comment\n[";
        assert_eq!(strip_shebang(input), None)
    }

    #[test]
    fn test_invalid_shebang_another_comment() {
        let input = "#!/*bin/ami/a/comment*/\n[attribute";
        assert_eq!(strip_shebang(input), None)
    }

    #[test]
    fn test_shebang_valid_rust_after() {
        let input = "#!/*bin/ami/a/comment*/\npub fn main() {}";
        assert_eq!(strip_shebang(input), Some(23))
    }

    #[test]
    fn test_shebang_followed_by_attrib() {
        let input = "#!/bin/rust-scripts\n#![allow_unused(true)]";
        assert_eq!(strip_shebang(input), Some(19));
    }
}
<commit_msg>Minor, remove double nesting of a test module<commit_after>use super::*;

fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option<RawStrError>) {
    let s = &format!("r{}", s);
    let mut cursor = Cursor::new(s);
    cursor.bump();
    let (n_hashes, err) = cursor.raw_double_quoted_string(0);
    assert_eq!(n_hashes, expected_hashes);
    assert_eq!(err, expected_err);
}

#[test]
fn test_naked_raw_str() {
    check_raw_str(r#""abc""#, 0, None);
}

#[test]
fn test_raw_no_start() {
    check_raw_str(r##""abc"#"##, 0, None);
}

#[test]
fn test_too_many_terminators() {
    // this error is handled in the parser later
    check_raw_str(r###"#"abc"##"###, 1, None);
}

#[test]
fn test_unterminated() {
    check_raw_str(
        r#"#"abc"#,
        1,
        Some(RawStrError::NoTerminator { expected: 1, found: 0, possible_terminator_offset: None }),
    );
    check_raw_str(
        r###"##"abc"#"###,
        2,
        Some(RawStrError::NoTerminator {
            expected: 2,
            found: 1,
            possible_terminator_offset: Some(7),
        }),
    );
    // We're looking for "# not just any #
    check_raw_str(
        r###"##"abc#"###,
        2,
        Some(RawStrError::NoTerminator { expected: 2, found: 0, possible_terminator_offset: None }),
    )
}

#[test]
fn test_invalid_start() {
    check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' }));
}

#[test]
fn test_unterminated_no_pound() {
    // https://github.com/rust-lang/rust/issues/70677
    check_raw_str(
        r#"""#,
        0,
        Some(RawStrError::NoTerminator { expected: 0, found: 0, possible_terminator_offset: None }),
    );
}

#[test]
fn test_valid_shebang() {
    // https://github.com/rust-lang/rust/issues/70528
    let input = "#!/usr/bin/rustrun\nlet x = 5;";
    assert_eq!(strip_shebang(input), Some(18));
}

#[test]
fn test_invalid_shebang_valid_rust_syntax() {
    // https://github.com/rust-lang/rust/issues/70528
    let input = "#!    [bad_attribute]";
    assert_eq!(strip_shebang(input), None);
}

#[test]
fn test_shebang_second_line() {
    // Because shebangs are interpreted by the kernel, they must be on the first line
    let input = "\n#!/bin/bash";
    assert_eq!(strip_shebang(input), None);
}

#[test]
fn test_shebang_space() {
    let input = "#!    /bin/bash";
    assert_eq!(strip_shebang(input), Some(input.len()));
}

#[test]
fn test_shebang_empty_shebang() {
    let input = "#!    \n[attribute(foo)]";
    assert_eq!(strip_shebang(input), None);
}

#[test]
fn test_invalid_shebang_comment() {
    let input = "#!//bin/ami/a/comment\n[";
    assert_eq!(strip_shebang(input), None)
}

#[test]
fn test_invalid_shebang_another_comment() {
    let input = "#!/*bin/ami/a/comment*/\n[attribute";
    assert_eq!(strip_shebang(input), None)
}

#[test]
fn test_shebang_valid_rust_after() {
    let input = "#!/*bin/ami/a/comment*/\npub fn main() {}";
    assert_eq!(strip_shebang(input), Some(23))
}

#[test]
fn test_shebang_followed_by_attrib() {
    let input = "#!/bin/rust-scripts\n#![allow_unused(true)]";
    assert_eq!(strip_shebang(input), Some(19));
}
<|endoftext|>


<commit_before>use crate::dictionary::CONF_LINE_SECRET_MODIFIER_LINEBREAK1;
use crate::dictionary::CONF_LINE_SECRET_MODIFIER_LINEBREAK2;
use crate::dictionary::CONF_LINE_SECRET_MODIFIER_VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// The character type.
#[derive(Clone, Debug, PartialEq)]
enum HangmanCharType {
    Visible,
    Hidden,
    Formatter,
    Ignored,
}

/// One character of the secret string.
#[derive(Clone, Debug, PartialEq)]
struct HangmanChar {
    character: char,
    chartype: HangmanCharType,
}

/// The secret
#[derive(Debug, PartialEq)]
pub struct Secret {
    hangman_chars: Vec<HangmanChar>,
    chars_to_guess: usize,
}

impl Secret {
    /// Constructor.
    pub fn new(secretstr: &str) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let mut whitespace_on = false;
        let mut visible_on = false;
        let w: Vec<HangmanChar> = secretstr
            .chars()
            // For every `_` found flip `visible_on`.
            // CONF_LINE_SECRET_MODIFIER__LINEBREAK1 found, set `whitespace_on=true`.
            // Non whitespace found, set `whitespace_on=false`.
            .map(|c| {
                if whitespace_on && !c.is_whitespace() {
                    whitespace_on = false;
                };
                if (c == CONF_LINE_SECRET_MODIFIER_LINEBREAK1)
                    || (c == CONF_LINE_SECRET_MODIFIER_LINEBREAK2)
                {
                    whitespace_on = true;
                };
                visible_on ^= c == CONF_LINE_SECRET_MODIFIER_VISIBLE;
                let ct = match (c, visible_on, whitespace_on) {
                    (CONF_LINE_SECRET_MODIFIER_VISIBLE, _, _) => HangmanCharType::Formatter,
                    (CONF_LINE_SECRET_MODIFIER_LINEBREAK1, _, _) => HangmanCharType::Formatter,
                    (CONF_LINE_SECRET_MODIFIER_LINEBREAK2, _, _) => HangmanCharType::Formatter,
                    (_, _, true) => HangmanCharType::Ignored,
                    (_, true, false) => HangmanCharType::Visible,
                    (_, false, false) => HangmanCharType::Hidden,
                };
                HangmanChar {
                    character: c,
                    chartype: ct,
                }
            })
            .collect();

        let chars_to_guess = w
            .iter()
            .filter(|hc| matches!(hc.chartype, HangmanCharType::Hidden))
            .count();

        Self {
            hangman_chars: w,
            chars_to_guess,
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, character: char) -> bool {
        let mut found = false;
        for h_char in &mut self.hangman_chars {
            if matches!(h_char.chartype, HangmanCharType::Hidden)
                && h_char.character.eq_ignore_ascii_case(&character)
            {
                h_char.chartype = HangmanCharType::Visible;
                found = true;
            }
        }

        found
    }

    /// We disclose all characters when all lives are used and the
    /// game is over.
    pub fn disclose_all(&mut self) {
        // Disclose the secret
        for hc in &mut self
            .hangman_chars
            .iter_mut()
            .filter(|c| matches!(c.chartype, HangmanCharType::Hidden))
        {
            hc.chartype = HangmanCharType::Visible;
        }
    }

    /// Method used to find out if the user has won.
    pub fn is_fully_disclosed(&self) -> bool {
        !self
            .hangman_chars
            .iter()
            .filter(|&c| matches!(c.chartype, HangmanCharType::Hidden))
            .any(|c| matches!(c.chartype, HangmanCharType::Hidden))
    }

    /// Information used to calculate how much of the image should
    /// be disclosed.
    pub fn hidden_chars(&self) -> usize {
        self.hangman_chars
            .iter()
            .filter(|hc| matches!(hc.chartype, HangmanCharType::Hidden))
            .count()
    }

    /// How many hidden chars we had at the beginning of the game?
    pub fn chars_to_guess(&self) -> usize {
        self.chars_to_guess
    }

    /// Used in case the secret was not guessed and we want to inject
    /// it to the dictionary again.
    pub fn to_raw_string(&self) -> String {
        self.hangman_chars.iter().map(|hc| hc.character).collect()
    }
}

impl fmt::Display for Secret {
    /// Graphical representation of the secret taking into account the
    /// game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut linebreak = false;
        let mut n = 1;
        for c in self.hangman_chars.iter() {
            if n >= LINE_WIDTH {
                linebreak = true
            };
            if matches!(c.chartype, HangmanCharType::Formatter)
                && (c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK1
                    || c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK2)
            {
                linebreak = true
            };

            if linebreak
                && (!matches!(c.chartype, HangmanCharType::Formatter) && (c.character == ' ')
                    || (matches!(c.chartype, HangmanCharType::Formatter)
                        && ((c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK1)
                            || (c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK2))))
            {
                linebreak = false;
                n = 0;
                writeln!(f, "")?;
            } else {
                match c.chartype {
                    HangmanCharType::Visible => {
                        write!(f, " {}", c.character)?;
                        n += 1;
                    }
                    HangmanCharType::Hidden => {
                        write!(f, " _")?;
                        n += 1;
                    }
                    HangmanCharType::Formatter => {}
                    HangmanCharType::Ignored => {}
                };
            }
        }
        writeln!(f)
    }
}

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

    /// Game simulation
    #[test]
    fn test_secret() {
        let mut secret = Secret::new("_ab _cd");

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('x');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('d');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ d\n");
        assert_eq!(secret.hidden_chars(), 1);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   c d\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
    }
    #[test]
    fn test_secret_linebreak() {
        let mut secret = Secret::new("_abc|def _hij|klm");
        assert_eq!(secret.to_string(), " a b c\n d e f   _ _ _\n _ _ _\n");
        assert_eq!(secret.to_raw_string(), "_abc|def _hij|klm");
        assert_eq!(secret.hidden_chars(), 6);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_string(), " a b c\n d e f   h i j\n k l m\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
        assert_eq!(secret.to_raw_string(), "_abc|def _hij|klm");

        let secret = Secret::new("_123456789012345 789012345 789012_");
        assert_eq!(
            secret.to_string(),
            " 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5   7 8 9 0 1 2 3 4 5\n 7 8 9 0 1 2\n"
        );

        let secret = Secret::new("_abc|  def _hij| \n  klm");
        assert_eq!(secret.to_string(), " a b c\n d e f   _ _ _\n\n _ _ _\n");
        assert_eq!(secret.to_raw_string(), "_abc|  def _hij| \n  klm");
        assert_eq!(secret.hidden_chars(), 6);
        assert!(!secret.is_fully_disclosed());
    }
}
<commit_msg>Simplify expression<commit_after>use crate::dictionary::CONF_LINE_SECRET_MODIFIER_LINEBREAK1;
use crate::dictionary::CONF_LINE_SECRET_MODIFIER_LINEBREAK2;
use crate::dictionary::CONF_LINE_SECRET_MODIFIER_VISIBLE;
use std::fmt;

/// Defines the line-break position when displaying the secret string.
const LINE_WIDTH: usize = 20;

/// The character type.
#[derive(Clone, Debug, PartialEq)]
enum HangmanCharType {
    Visible,
    Hidden,
    Formatter,
    Ignored,
}

/// One character of the secret string.
#[derive(Clone, Debug, PartialEq)]
struct HangmanChar {
    character: char,
    chartype: HangmanCharType,
}

/// The secret
#[derive(Debug, PartialEq)]
pub struct Secret {
    hangman_chars: Vec<HangmanChar>,
    chars_to_guess: usize,
}

impl Secret {
    /// Constructor.
    pub fn new(secretstr: &str) -> Self {
        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
        let mut whitespace_on = false;
        let mut visible_on = false;
        let w: Vec<HangmanChar> = secretstr
            .chars()
            // For every `_` found flip `visible_on`.
            // CONF_LINE_SECRET_MODIFIER__LINEBREAK1 found, set `whitespace_on=true`.
            // Non whitespace found, set `whitespace_on=false`.
            .map(|c| {
                if whitespace_on && !c.is_whitespace() {
                    whitespace_on = false;
                };
                if (c == CONF_LINE_SECRET_MODIFIER_LINEBREAK1)
                    || (c == CONF_LINE_SECRET_MODIFIER_LINEBREAK2)
                {
                    whitespace_on = true;
                };
                visible_on ^= c == CONF_LINE_SECRET_MODIFIER_VISIBLE;
                let ct = match (c, visible_on, whitespace_on) {
                    (CONF_LINE_SECRET_MODIFIER_VISIBLE, _, _) => HangmanCharType::Formatter,
                    (CONF_LINE_SECRET_MODIFIER_LINEBREAK1, _, _) => HangmanCharType::Formatter,
                    (CONF_LINE_SECRET_MODIFIER_LINEBREAK2, _, _) => HangmanCharType::Formatter,
                    (_, _, true) => HangmanCharType::Ignored,
                    (_, true, false) => HangmanCharType::Visible,
                    (_, false, false) => HangmanCharType::Hidden,
                };
                HangmanChar {
                    character: c,
                    chartype: ct,
                }
            })
            .collect();

        let chars_to_guess = w
            .iter()
            .filter(|hc| matches!(hc.chartype, HangmanCharType::Hidden))
            .count();

        Self {
            hangman_chars: w,
            chars_to_guess,
        }
    }

    /// Process a guess and modify the game state.
    pub fn guess(&mut self, character: char) -> bool {
        let mut found = false;
        for h_char in &mut self.hangman_chars {
            if matches!(h_char.chartype, HangmanCharType::Hidden)
                && h_char.character.eq_ignore_ascii_case(&character)
            {
                h_char.chartype = HangmanCharType::Visible;
                found = true;
            }
        }

        found
    }

    /// We disclose all characters when all lives are used and the
    /// game is over.
    pub fn disclose_all(&mut self) {
        // Disclose the secret
        for hc in &mut self
            .hangman_chars
            .iter_mut()
            .filter(|c| matches!(c.chartype, HangmanCharType::Hidden))
        {
            hc.chartype = HangmanCharType::Visible;
        }
    }

    /// Method used to find out if the user has won.
    pub fn is_fully_disclosed(&self) -> bool {
        !self
            .hangman_chars
            .iter()
            .filter(|&c| matches!(c.chartype, HangmanCharType::Hidden))
            .any(|c| matches!(c.chartype, HangmanCharType::Hidden))
    }

    /// Information used to calculate how much of the image should
    /// be disclosed.
    pub fn hidden_chars(&self) -> usize {
        self.hangman_chars
            .iter()
            .filter(|hc| matches!(hc.chartype, HangmanCharType::Hidden))
            .count()
    }

    /// How many hidden chars we had at the beginning of the game?
    pub fn chars_to_guess(&self) -> usize {
        self.chars_to_guess
    }

    /// Used in case the secret was not guessed and we want to inject
    /// it to the dictionary again.
    pub fn to_raw_string(&self) -> String {
        self.hangman_chars.iter().map(|hc| hc.character).collect()
    }
}

impl fmt::Display for Secret {
    /// Graphical representation of the secret taking into account the
    /// game state.
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut linebreak = false;
        let mut n = 1;
        for c in self.hangman_chars.iter() {
            if n >= LINE_WIDTH {
                linebreak = true
            };
            if matches!(c.chartype, HangmanCharType::Formatter)
                && (c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK1
                    || c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK2)
            {
                linebreak = true
            };

            if linebreak
                && (!matches!(c.chartype, HangmanCharType::Formatter) && (c.character == ' ')
                    || (matches!(c.chartype, HangmanCharType::Formatter)
                        && ((c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK1)
                            || (c.character == CONF_LINE_SECRET_MODIFIER_LINEBREAK2))))
            {
                linebreak = false;
                n = 0;
                writeln!(f)?;
            } else {
                match c.chartype {
                    HangmanCharType::Visible => {
                        write!(f, " {}", c.character)?;
                        n += 1;
                    }
                    HangmanCharType::Hidden => {
                        write!(f, " _")?;
                        n += 1;
                    }
                    HangmanCharType::Formatter => {}
                    HangmanCharType::Ignored => {}
                };
            }
        }
        writeln!(f)
    }
}

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

    /// Game simulation
    #[test]
    fn test_secret() {
        let mut secret = Secret::new("_ab _cd");

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('x');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ _\n");
        assert_eq!(secret.hidden_chars(), 2);
        assert!(!secret.is_fully_disclosed());

        secret.guess('d');

        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   _ d\n");
        assert_eq!(secret.hidden_chars(), 1);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_raw_string(), "_ab _cd");
        assert_eq!(format!("{}", secret), " a b   c d\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
    }
    #[test]
    fn test_secret_linebreak() {
        let mut secret = Secret::new("_abc|def _hij|klm");
        assert_eq!(secret.to_string(), " a b c\n d e f   _ _ _\n _ _ _\n");
        assert_eq!(secret.to_raw_string(), "_abc|def _hij|klm");
        assert_eq!(secret.hidden_chars(), 6);
        assert!(!secret.is_fully_disclosed());

        secret.disclose_all();
        assert_eq!(secret.to_string(), " a b c\n d e f   h i j\n k l m\n");
        assert_eq!(secret.hidden_chars(), 0);
        assert!(secret.is_fully_disclosed());
        assert_eq!(secret.to_raw_string(), "_abc|def _hij|klm");

        let secret = Secret::new("_123456789012345 789012345 789012_");
        assert_eq!(
            secret.to_string(),
            " 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5   7 8 9 0 1 2 3 4 5\n 7 8 9 0 1 2\n"
        );

        let secret = Secret::new("_abc|  def _hij| \n  klm");
        assert_eq!(secret.to_string(), " a b c\n d e f   _ _ _\n\n _ _ _\n");
        assert_eq!(secret.to_raw_string(), "_abc|  def _hij| \n  klm");
        assert_eq!(secret.hidden_chars(), 6);
        assert!(!secret.is_fully_disclosed());
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;

use crate::authentication::Credentials;
use crate::spotify_id::FileId;
use crate::volume::Volume;

#[derive(Clone)]
pub struct Cache {
    root: PathBuf,
    use_audio_cache: bool,
}

fn mkdir_existing(path: &Path) -> io::Result<()> {
    fs::create_dir(path).or_else(|err| {
        if err.kind() == io::ErrorKind::AlreadyExists {
            Ok(())
        } else {
            Err(err)
        }
    })
}

impl Cache {
    pub fn new(location: PathBuf, use_audio_cache: bool) -> Cache {
        mkdir_existing(&location).unwrap();
        mkdir_existing(&location.join("files")).unwrap();

        Cache {
            root: location,
            use_audio_cache: use_audio_cache,
        }
    }
}

impl Cache {
    fn credentials_path(&self) -> PathBuf {
        self.root.join("credentials.json")
    }

    pub fn credentials(&self) -> Option<Credentials> {
        let path = self.credentials_path();
        Credentials::from_file(path)
    }

    pub fn save_credentials(&self, cred: &Credentials) {
        let path = self.credentials_path();
        cred.save_to_file(&path);
    }
}

// cache volume to root/volume
impl Cache {
    fn volume_path(&self) -> PathBuf {
        self.root.join("volume")
    }

    pub fn volume(&self) -> Option<u16> {
        let path = self.volume_path();
        Volume::from_file(path)
    }

    pub fn save_volume(&self, volume: Volume) {
        let path = self.volume_path();
        volume.save_to_file(&path);
    }
}

impl Cache {
    fn file_path(&self, file: FileId) -> PathBuf {
        let name = file.to_base16();
        self.root.join("files").join(&name[0..2]).join(&name[2..])
    }

    pub fn file(&self, file: FileId) -> Option<File> {
        File::open(self.file_path(file)).ok()
    }

    pub fn save_file(&self, file: FileId, contents: &mut dyn Read) {
        if self.use_audio_cache {
            let path = self.file_path(file);
            mkdir_existing(path.parent().unwrap()).unwrap();

            let file_create = File::create(path);
            match file_create {
                Ok(mut cache_file) => {
                    ::std::io::copy(contents, &mut cache_file).unwrap();
                }
                Err(_error) => {
                    ::std::fs::remove_dir_all(&self.root.join("files")).unwrap();
                    mkdir_existing(&self.root.join("files")).unwrap();

                    let path = self.file_path(file);
                    mkdir_existing(path.parent().unwrap()).unwrap();
                    let mut cache_file = File::create(path).unwrap();
                    ::std::io::copy(contents, &mut cache_file).unwrap();
                }
            };
        }
    }
}
<commit_msg>Main issue is probably when copying the content to the file, not during the file creation, but handle both cases just to be sure<commit_after>use std::fs;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;

use crate::authentication::Credentials;
use crate::spotify_id::FileId;
use crate::volume::Volume;

#[derive(Clone)]
pub struct Cache {
    root: PathBuf,
    use_audio_cache: bool,
}

fn mkdir_existing(path: &Path) -> io::Result<()> {
    fs::create_dir(path).or_else(|err| {
        if err.kind() == io::ErrorKind::AlreadyExists {
            Ok(())
        } else {
            Err(err)
        }
    })
}

impl Cache {
    pub fn new(location: PathBuf, use_audio_cache: bool) -> Cache {
        mkdir_existing(&location).unwrap();
        mkdir_existing(&location.join("files")).unwrap();

        Cache {
            root: location,
            use_audio_cache: use_audio_cache,
        }
    }
}

impl Cache {
    fn credentials_path(&self) -> PathBuf {
        self.root.join("credentials.json")
    }

    pub fn credentials(&self) -> Option<Credentials> {
        let path = self.credentials_path();
        Credentials::from_file(path)
    }

    pub fn save_credentials(&self, cred: &Credentials) {
        let path = self.credentials_path();
        cred.save_to_file(&path);
    }
}

// cache volume to root/volume
impl Cache {
    fn volume_path(&self) -> PathBuf {
        self.root.join("volume")
    }

    pub fn volume(&self) -> Option<u16> {
        let path = self.volume_path();
        Volume::from_file(path)
    }

    pub fn save_volume(&self, volume: Volume) {
        let path = self.volume_path();
        volume.save_to_file(&path);
    }
}

impl Cache {
    fn file_path(&self, file: FileId) -> PathBuf {
        let name = file.to_base16();
        self.root.join("files").join(&name[0..2]).join(&name[2..])
    }

    pub fn file(&self, file: FileId) -> Option<File> {
        File::open(self.file_path(file)).ok()
    }

    pub fn save_file(&self, file: FileId, contents: &mut dyn Read) {
        if self.use_audio_cache {
            let path = self.file_path(file);
            mkdir_existing(path.parent().unwrap()).unwrap();

            let mut cache_file = File::create(path).unwrap_or_else(|_e| {
                ::std::fs::remove_dir_all(&self.root.join("files")).unwrap();
                mkdir_existing(&self.root.join("files")).unwrap();

                let path = self.file_path(file);
                mkdir_existing(path.parent().unwrap()).unwrap();
                File::create(path).unwrap()
            });
            ::std::io::copy(contents, &mut cache_file).unwrap_or_else(|_e| {
                ::std::fs::remove_dir_all(&self.root.join("files")).unwrap();
                mkdir_existing(&self.root.join("files")).unwrap();

                let path = self.file_path(file);
                mkdir_existing(path.parent().unwrap()).unwrap();
                let mut file = File::create(path).unwrap();
                ::std::io::copy(contents, &mut file).unwrap()
            });
        }
    }
}
<|endoftext|>


<commit_before>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode, ChatAction};
    use teleborg::objects::{Button, Markup};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_url_chat_actions() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let message = bot.send_chat_action(&chat_id, &ChatAction::FindLocation);
        println!("{:?}", &message);
        assert!(message.is_ok());
    }
}<commit_msg>removed Markup and Button imports<commit_after>extern crate teleborg;
extern crate reqwest;

#[cfg(test)]
mod tests {
    use teleborg::{Bot, ParseMode, ChatAction};
    use reqwest::Client;

    use std::env;

    const BASE_URL: &'static str = "https://api.telegram.org/bot";

    fn setup() -> (String, i64) {
        let token = env::var("TELEGRAM_BOT_TOKEN")
            .ok()
            .expect("Can't find TELEGRAM_BOT_TOKEN env variable");
        let chat_id = env::var("TELEGRAM_CHAT_ID")
            .ok()
            .expect("Can't find TELEGRAM_CHAT_ID env variable")
            .parse::<i64>()
            .unwrap();
        (token, chat_id)
    }

    #[test]
    fn test_url_chat_actions() {
        let (token, chat_id) = setup();
        let bot_url = [BASE_URL, &token].concat();
        let bot = Bot::new(bot_url).unwrap();

        let message = bot.send_chat_action(&chat_id, &ChatAction::FindLocation);
        println!("{:?}", &message);
        assert!(message.is_ok());
    }
}<|endoftext|>


<commit_before>use super::generate;

#[test]
fn partial_overlap() {
    let set1 = generate(vec![('3', '5'), ('4', '6')]);
    let set2 = generate(vec![('3', '5'), ('1', '4')]);

    let other1 = generate(vec![('3', '6')]);
    let other2 = generate(vec![('1', '5')]);

    assert_eq!(other1, set1);
    assert_eq!(other2, set2);
}
#[test]
fn subset() {
    let set1 = generate(vec![('1', '5'), ('2', '3')]); // Complete overlap.
    let set2 = generate(vec![('1', '5'), ('1', '3')]); // Left is exact.
    let set3 = generate(vec![('1', '5'), ('2', '5')]); // Right is exact.
    let set4 = generate(vec![('1', '5'), ('1', '5')]); // Both are exact.

    let other = generate(vec![('1', '5')]);

    assert_eq!(set1, other);
    assert_eq!(set2, other);
    assert_eq!(set3, other);
    assert_eq!(set4, other);
}
#[test]
fn superset() {
    let set   = generate(vec![('3', '5'), ('2', '6')]);
    let other = generate(vec![('2', '6')]);

    assert_eq!(other, set);
}
#[test]
#[should_panic]
fn panic_on_decreasing_order() {
    generate(vec![('5', '3')]);
}
<commit_msg>Clean up insert tests<commit_after>use super::generate;

#[test]
fn partial_overlap() {
    let left  = generate(vec![('3', '5'), ('4', '6')]);
    let right = generate(vec![('3', '5'), ('1', '4')]);

    let other_left  = generate(vec![('3', '6')]);
    let other_right = generate(vec![('1', '5')]);

    assert_eq!(left, other_left);
    assert_eq!(right, other_right);
}
#[test]
fn subset() {
    let inner = generate(vec![('1', '5'), ('2', '3')]);
    let left  = generate(vec![('1', '5'), ('1', '3')]);
    let right = generate(vec![('1', '5'), ('2', '5')]);
    let both  = generate(vec![('1', '5'), ('1', '5')]);

    let other = generate(vec![('1', '5')]);

    assert_eq!(inner, other);
    assert_eq!(left, other);
    assert_eq!(right, other);
    assert_eq!(both, other);
}
#[test]
fn superset() {
    let set   = generate(vec![('3', '5'), ('2', '6')]);
    let other = generate(vec![('2', '6')]);

    assert_eq!(other, set);
}
#[test]
// Useless test but added for completeness.
fn disjoint() {
    let set   = generate(vec![('2', '4'), ('6', '8')]);
    let other = generate(vec![('2', '4'), ('6', '8')]);

    assert_eq!(other, set);
}
#[test]
#[should_panic]
fn panic_on_decreasing_order() {
    generate(vec![('5', '3')]);
}
<|endoftext|>


<commit_before>#![warn(clippy::similar_names)]
#![allow(unused, clippy::println_empty_string)]

struct Foo {
    apple: i32,
    bpple: i32,
}

fn main() {
    let specter: i32;
    let spectre: i32;

    let apple: i32;

    let bpple: i32;

    let cpple: i32;

    let a_bar: i32;
    let b_bar: i32;
    let c_bar: i32;

    let items = [5];
    for item in &items {
        loop {}
    }

    let foo_x: i32;
    let foo_y: i32;

    let rhs: i32;
    let lhs: i32;

    let bla_rhs: i32;
    let bla_lhs: i32;

    let blubrhs: i32;
    let blublhs: i32;

    let blubx: i32;
    let bluby: i32;

    let cake: i32;
    let cakes: i32;
    let coke: i32;

    match 5 {
        cheese @ 1 => {},
        rabbit => panic!(),
    }
    let cheese: i32;
    match (42, 43) {
        (cheese1, 1) => {},
        (cheese2, 2) => panic!(),
        _ => println!(""),
    }
    let ipv4: i32;
    let ipv6: i32;
    let abcd1: i32;
    let abdc2: i32;
    let xyz1abc: i32;
    let xyz2abc: i32;
    let xyzeabc: i32;

    let parser: i32;
    let parsed: i32;
    let parsee: i32;

    let setter: i32;
    let getter: i32;
    let tx1: i32;
    let rx1: i32;
    let tx_cake: i32;
    let rx_cake: i32;

    // names often used in win32 code (for example WindowProc)
    let wparam: i32;
    let lparam: i32;
}

fn foo() {
    let Foo { apple, bpple } = unimplemented!();
    let Foo {
        apple: spring,
        bpple: sprang,
    } = unimplemented!();
}

// false positive similar_names (#3057, #2651)
// clippy claimed total_reg_src_size and total_size and
// numb_reg_src_checkouts and total_bin_size were similar
#[derive(Debug, Clone)]
pub(crate) struct DirSizes {
    pub(crate) total_size: u64,
    pub(crate) numb_bins: u64,
    pub(crate) total_bin_size: u64,
    pub(crate) total_reg_size: u64,
    pub(crate) total_git_db_size: u64,
    pub(crate) total_git_repos_bare_size: u64,
    pub(crate) numb_git_repos_bare_repos: u64,
    pub(crate) numb_git_checkouts: u64,
    pub(crate) total_git_chk_size: u64,
    pub(crate) total_reg_cache_size: u64,
    pub(crate) total_reg_src_size: u64,
    pub(crate) numb_reg_cache_entries: u64,
    pub(crate) numb_reg_src_checkouts: u64,
}

fn ignore_underscore_prefix() {
    let hello: ();
    let _hello: ();
}
<commit_msg>Add test for similar names "iter" and "item"<commit_after>#![warn(clippy::similar_names)]
#![allow(unused, clippy::println_empty_string)]

struct Foo {
    apple: i32,
    bpple: i32,
}

fn main() {
    let specter: i32;
    let spectre: i32;

    let apple: i32;

    let bpple: i32;

    let cpple: i32;

    let a_bar: i32;
    let b_bar: i32;
    let c_bar: i32;

    let items = [5];
    for item in &items {
        loop {}
    }

    let foo_x: i32;
    let foo_y: i32;

    let rhs: i32;
    let lhs: i32;

    let bla_rhs: i32;
    let bla_lhs: i32;

    let blubrhs: i32;
    let blublhs: i32;

    let blubx: i32;
    let bluby: i32;

    let cake: i32;
    let cakes: i32;
    let coke: i32;

    match 5 {
        cheese @ 1 => {},
        rabbit => panic!(),
    }
    let cheese: i32;
    match (42, 43) {
        (cheese1, 1) => {},
        (cheese2, 2) => panic!(),
        _ => println!(""),
    }
    let ipv4: i32;
    let ipv6: i32;
    let abcd1: i32;
    let abdc2: i32;
    let xyz1abc: i32;
    let xyz2abc: i32;
    let xyzeabc: i32;

    let parser: i32;
    let parsed: i32;
    let parsee: i32;

    let setter: i32;
    let getter: i32;
    let tx1: i32;
    let rx1: i32;
    let tx_cake: i32;
    let rx_cake: i32;

    // names often used in win32 code (for example WindowProc)
    let wparam: i32;
    let lparam: i32;

    let iter: i32;
    let item: i32;
}

fn foo() {
    let Foo { apple, bpple } = unimplemented!();
    let Foo {
        apple: spring,
        bpple: sprang,
    } = unimplemented!();
}

// false positive similar_names (#3057, #2651)
// clippy claimed total_reg_src_size and total_size and
// numb_reg_src_checkouts and total_bin_size were similar
#[derive(Debug, Clone)]
pub(crate) struct DirSizes {
    pub(crate) total_size: u64,
    pub(crate) numb_bins: u64,
    pub(crate) total_bin_size: u64,
    pub(crate) total_reg_size: u64,
    pub(crate) total_git_db_size: u64,
    pub(crate) total_git_repos_bare_size: u64,
    pub(crate) numb_git_repos_bare_repos: u64,
    pub(crate) numb_git_checkouts: u64,
    pub(crate) total_git_chk_size: u64,
    pub(crate) total_reg_cache_size: u64,
    pub(crate) total_reg_src_size: u64,
    pub(crate) numb_reg_cache_entries: u64,
    pub(crate) numb_reg_src_checkouts: u64,
}

fn ignore_underscore_prefix() {
    let hello: ();
    let _hello: ();
}
<|endoftext|>


<commit_before>#![allow(clippy::exhaustive_structs)]

use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::Event;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::{
    EphemeralRoomEventContent, GlobalAccountDataEventContent, MessageEventContent,
    RedactedMessageEventContent, RedactedStateEventContent, RedactedUnsigned,
    RoomAccountDataEventContent, StateEventContent, ToDeviceEventContent, Unsigned,
};

/// A global account data event.
#[derive(Clone, Debug, Event)]
pub struct GlobalAccountDataEvent<C: GlobalAccountDataEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// A room account data event.
#[derive(Clone, Debug, Event)]
pub struct RoomAccountDataEvent<C: RoomAccountDataEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// An ephemeral room event.
#[derive(Clone, Debug, Event)]
pub struct EphemeralRoomEvent<C: EphemeralRoomEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,
}

/// An ephemeral room event without a `room_id`.
#[derive(Clone, Debug, Event)]
pub struct SyncEphemeralRoomEvent<C: EphemeralRoomEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// A message event.
///
/// `MessageEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct MessageEvent<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A message event without a `room_id`.
///
/// `SyncMessageEvent` implements the comparison traits using only the `event_id` field, a sorted
/// list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct SyncMessageEvent<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A redacted message event.
///
/// `RedactedMessageEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedMessageEvent<C: RedactedMessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A redacted message event without a `room_id`.
///
/// `RedactedSyncMessageEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedSyncMessageEvent<C: RedactedMessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A state event.
///
/// `StateEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct StateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Optional previous content for this event.
    pub prev_content: Option<C>,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A state event without a `room_id`.
///
/// `SyncStateEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct SyncStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Optional previous content for this event.
    pub prev_content: Option<C>,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A stripped-down state event, used for previews of rooms the user has been invited to.
#[derive(Clone, Debug, Event)]
pub struct StrippedStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,
}

/// A minimal state event, used for creating a new room.
#[derive(Clone, Debug, Event)]
pub struct InitialStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    ///
    /// Defaults to the empty string.
    #[ruma_event(default)]
    pub state_key: String,
}

/// A redacted state event.
///
/// `RedactedStateEvent` implements the comparison traits using only the `event_id` field, a sorted
/// list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedStateEvent<C: RedactedStateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A redacted state event without a `room_id`.
///
/// `RedactedSyncStateEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedSyncStateEvent<C: RedactedStateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// An event sent using send-to-device messaging.
#[derive(Clone, Debug, Event)]
pub struct ToDeviceEvent<C: ToDeviceEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,
}

/// The decrypted payload of an `m.olm.v1.curve25519-aes-sha2` event.
#[derive(Clone, Debug, Event)]
pub struct DecryptedOlmV1Event<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// The fully-qualified ID of the intended recipient this event.
    pub recipient: UserId,

    /// The recipient's ed25519 key.
    pub recipient_keys: OlmV1Keys,

    /// The sender's ed25519 key.
    pub keys: OlmV1Keys,
}

/// Public keys used for an `m.olm.v1.curve25519-aes-sha2` event.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OlmV1Keys {
    /// An ed25519 key.
    ed25519: String,
}

/// The decrypted payload of an `m.megolm.v1.aes-sha2` event.
#[derive(Clone, Debug, Event)]
pub struct DecryptedMegolmV1Event<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The ID of the room associated with the event.
    pub room_id: RoomId,
}
<commit_msg>events: Make OlmV1Keys::ed25519 public<commit_after>#![allow(clippy::exhaustive_structs)]

use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::Event;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::{
    EphemeralRoomEventContent, GlobalAccountDataEventContent, MessageEventContent,
    RedactedMessageEventContent, RedactedStateEventContent, RedactedUnsigned,
    RoomAccountDataEventContent, StateEventContent, ToDeviceEventContent, Unsigned,
};

/// A global account data event.
#[derive(Clone, Debug, Event)]
pub struct GlobalAccountDataEvent<C: GlobalAccountDataEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// A room account data event.
#[derive(Clone, Debug, Event)]
pub struct RoomAccountDataEvent<C: RoomAccountDataEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// An ephemeral room event.
#[derive(Clone, Debug, Event)]
pub struct EphemeralRoomEvent<C: EphemeralRoomEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,
}

/// An ephemeral room event without a `room_id`.
#[derive(Clone, Debug, Event)]
pub struct SyncEphemeralRoomEvent<C: EphemeralRoomEventContent> {
    /// Data specific to the event type.
    pub content: C,
}

/// A message event.
///
/// `MessageEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct MessageEvent<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A message event without a `room_id`.
///
/// `SyncMessageEvent` implements the comparison traits using only the `event_id` field, a sorted
/// list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct SyncMessageEvent<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A redacted message event.
///
/// `RedactedMessageEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedMessageEvent<C: RedactedMessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A redacted message event without a `room_id`.
///
/// `RedactedSyncMessageEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedSyncMessageEvent<C: RedactedMessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A state event.
///
/// `StateEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct StateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Optional previous content for this event.
    pub prev_content: Option<C>,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A state event without a `room_id`.
///
/// `SyncStateEvent` implements the comparison traits using only the `event_id` field, a sorted list
/// would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct SyncStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Optional previous content for this event.
    pub prev_content: Option<C>,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: Unsigned,
}

/// A stripped-down state event, used for previews of rooms the user has been invited to.
#[derive(Clone, Debug, Event)]
pub struct StrippedStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,
}

/// A minimal state event, used for creating a new room.
#[derive(Clone, Debug, Event)]
pub struct InitialStateEvent<C: StateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    ///
    /// Defaults to the empty string.
    #[ruma_event(default)]
    pub state_key: String,
}

/// A redacted state event.
///
/// `RedactedStateEvent` implements the comparison traits using only the `event_id` field, a sorted
/// list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedStateEvent<C: RedactedStateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// The ID of the room associated with this event.
    pub room_id: RoomId,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// A redacted state event without a `room_id`.
///
/// `RedactedSyncStateEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct RedactedSyncStateEvent<C: RedactedStateEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The globally unique event identifier for the user who sent the event.
    pub event_id: EventId,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// Timestamp in milliseconds on originating homeserver when this event was sent.
    pub origin_server_ts: MilliSecondsSinceUnixEpoch,

    /// A unique key which defines the overwriting semantics for this piece of room state.
    ///
    /// This is often an empty string, but some events send a `UserId` to show which user the event
    /// affects.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    pub unsigned: RedactedUnsigned,
}

/// An event sent using send-to-device messaging.
#[derive(Clone, Debug, Event)]
pub struct ToDeviceEvent<C: ToDeviceEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,
}

/// The decrypted payload of an `m.olm.v1.curve25519-aes-sha2` event.
#[derive(Clone, Debug, Event)]
pub struct DecryptedOlmV1Event<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The fully-qualified ID of the user who sent this event.
    pub sender: UserId,

    /// The fully-qualified ID of the intended recipient this event.
    pub recipient: UserId,

    /// The recipient's ed25519 key.
    pub recipient_keys: OlmV1Keys,

    /// The sender's ed25519 key.
    pub keys: OlmV1Keys,
}

/// Public keys used for an `m.olm.v1.curve25519-aes-sha2` event.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OlmV1Keys {
    /// An ed25519 key.
    pub ed25519: String,
}

/// The decrypted payload of an `m.megolm.v1.aes-sha2` event.
#[derive(Clone, Debug, Event)]
pub struct DecryptedMegolmV1Event<C: MessageEventContent> {
    /// Data specific to the event type.
    pub content: C,

    /// The ID of the room associated with the event.
    pub room_id: RoomId,
}
<|endoftext|>


<commit_before>//! Utilities to work with files, produced by macros.
use std::iter::successors;

use hir::{InFile, Origin};
use ra_db::FileId;
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange};

use crate::{db::RootDatabase, FileRange};

pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange {
    if let Some((range, Origin::Call)) = original_range_and_origin(db, node) {
        return range;
    }

    if let Some(expansion) = node.file_id.expansion_info(db) {
        if let Some(call_node) = expansion.call_node() {
            return FileRange {
                file_id: call_node.file_id.original_file(db),
                range: call_node.value.text_range(),
            };
        }
    }

    FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
}

fn original_range_and_origin(
    db: &RootDatabase,
    node: InFile<&SyntaxNode>,
) -> Option<(FileRange, Origin)> {
    let expansion = node.file_id.expansion_info(db)?;

    // the input node has only one token ?
    let single = node.value.first_token()? == node.value.last_token()?;

    // FIXME: We should handle recurside macro expansions
    let (range, origin) = node.value.descendants().find_map(|it| {
        let first = it.first_token()?;
        let last = it.last_token()?;

        if !single && first == last {
            return None;
        }

        // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
        let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
        let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;

        if first.file_id != last.file_id || first_origin != last_origin {
            return None;
        }

        // FIXME: Add union method in TextRange
        Some((
            first.with_value(union_range(first.value.text_range(), last.value.text_range())),
            first_origin,
        ))
    })?;

    return Some((
        FileRange { file_id: range.file_id.original_file(db), range: range.value },
        origin,
    ));

    fn union_range(a: TextRange, b: TextRange) -> TextRange {
        let start = a.start().min(b.start());
        let end = a.end().max(b.end());
        TextRange::from_to(start, end)
    }
}

pub(crate) fn descend_into_macros(
    db: &RootDatabase,
    file_id: FileId,
    token: SyntaxToken,
) -> InFile<SyntaxToken> {
    let src = InFile::new(file_id.into(), token);

    successors(Some(src), |token| {
        let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
        let tt = macro_call.token_tree()?;
        if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
            return None;
        }
        let source_analyzer =
            hir::SourceAnalyzer::new(db, token.with_value(token.value.parent()).as_ref(), None);
        let exp = source_analyzer.expand(db, token.with_value(&macro_call))?;
        exp.map_token_down(db, token.as_ref())
    })
    .last()
    .unwrap()
}
<commit_msg>Use src node for analyze source<commit_after>//! Utilities to work with files, produced by macros.
use std::iter::successors;

use hir::{InFile, Origin};
use ra_db::FileId;
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange};

use crate::{db::RootDatabase, FileRange};

pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange {
    if let Some((range, Origin::Call)) = original_range_and_origin(db, node) {
        return range;
    }

    if let Some(expansion) = node.file_id.expansion_info(db) {
        if let Some(call_node) = expansion.call_node() {
            return FileRange {
                file_id: call_node.file_id.original_file(db),
                range: call_node.value.text_range(),
            };
        }
    }

    FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
}

fn original_range_and_origin(
    db: &RootDatabase,
    node: InFile<&SyntaxNode>,
) -> Option<(FileRange, Origin)> {
    let expansion = node.file_id.expansion_info(db)?;

    // the input node has only one token ?
    let single = node.value.first_token()? == node.value.last_token()?;

    // FIXME: We should handle recurside macro expansions
    let (range, origin) = node.value.descendants().find_map(|it| {
        let first = it.first_token()?;
        let last = it.last_token()?;

        if !single && first == last {
            return None;
        }

        // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
        let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
        let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;

        if first.file_id != last.file_id || first_origin != last_origin {
            return None;
        }

        // FIXME: Add union method in TextRange
        Some((
            first.with_value(union_range(first.value.text_range(), last.value.text_range())),
            first_origin,
        ))
    })?;

    return Some((
        FileRange { file_id: range.file_id.original_file(db), range: range.value },
        origin,
    ));

    fn union_range(a: TextRange, b: TextRange) -> TextRange {
        let start = a.start().min(b.start());
        let end = a.end().max(b.end());
        TextRange::from_to(start, end)
    }
}

pub(crate) fn descend_into_macros(
    db: &RootDatabase,
    file_id: FileId,
    token: SyntaxToken,
) -> InFile<SyntaxToken> {
    let src = InFile::new(file_id.into(), token);

    let source_analyzer =
        hir::SourceAnalyzer::new(db, src.with_value(src.value.parent()).as_ref(), None);

    successors(Some(src), |token| {
        let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
        let tt = macro_call.token_tree()?;
        if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
            return None;
        }
        let exp = source_analyzer.expand(db, token.with_value(&macro_call))?;
        exp.map_token_down(db, token.as_ref())
    })
    .last()
    .unwrap()
}
<|endoftext|>