<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.

use std::{
    fmt::{Display, Formatter},
    result,
};

use kvm_bindings::KVM_API_VERSION;
use kvm_ioctls::Kvm;

/// Errors associated with the wrappers over KVM ioctls.
#[derive(Debug)]
pub enum Error {
    /// The host kernel reports an invalid KVM API version.
    KvmApiVersion(i32),
    /// Cannot initialize the KVM context due to missing capabilities.
    KvmCap(kvm_ioctls::Cap),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        use self::Error::*;

        match self {
            KvmApiVersion(v) => write!(
                f,
                "The host kernel reports an invalid KVM API version: {}",
                v
            ),
            KvmCap(cap) => write!(f, "Missing KVM capabilities: {:?}", cap),
        }
    }
}

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

/// Describes a KVM context that gets attached to the microVM.
/// It gives access to the functionality of the KVM wrapper as
/// long as every required KVM capability is present on the host.
pub struct KvmContext {
    kvm: Kvm,
    max_memslots: usize,
}

impl KvmContext {
    pub fn new() -> Result<Self> {
        use kvm_ioctls::Cap::*;
        let kvm = Kvm::new().expect("Error creating the Kvm object");

        // Check that KVM has the correct version.
        if kvm.get_api_version() != KVM_API_VERSION as i32 {
            return Err(Error::KvmApiVersion(kvm.get_api_version()));
        }

        // A list of KVM capabilities we want to check.
        #[cfg(target_arch = "x86_64")]
        let capabilities = vec![
            Irqchip,
            Ioeventfd,
            Irqfd,
            UserMemory,
            SetTssAddr,
            Pit2,
            PitState2,
            AdjustClock,
            Debugregs,
            MpState,
            VcpuEvents,
            Xcrs,
            Xsave,
            ExtCpuid,
        ];

        #[cfg(target_arch = "aarch64")]
        let capabilities = vec![
            Irqchip, Ioeventfd, Irqfd, UserMemory, ArmPsci02, DeviceCtrl, MpState,
        ];

        // Check that all desired capabilities are supported.
        match capabilities
            .iter()
            .find(|&capability| !kvm.check_extension(*capability))
        {
            None => {
                let max_memslots = kvm.get_nr_memslots();
                Ok(KvmContext { kvm, max_memslots })
            }

            Some(c) => Err(Error::KvmCap(*c)),
        }
    }

    pub fn fd(&self) -> &Kvm {
        &self.kvm
    }

    /// Get the maximum number of memory slots reported by this KVM context.
    pub fn max_memslots(&self) -> usize {
        self.max_memslots
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;

    use super::*;

    #[test]
    fn test_kvm_context() {
        use std::os::unix::fs::MetadataExt;
        use std::os::unix::io::{AsRawFd, FromRawFd};

        let c = KvmContext::new().unwrap();

        assert!(c.max_memslots() >= 32);

        let kvm = Kvm::new().unwrap();
        let f = unsafe { File::from_raw_fd(kvm.as_raw_fd()) };
        let m1 = f.metadata().unwrap();
        let m2 = File::open("/dev/kvm").unwrap().metadata().unwrap();

        assert_eq!(m1.dev(), m2.dev());
        assert_eq!(m1.ino(), m2.ino());
    }
}
<commit_msg>Add OneReg capability check for aarch64<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.

use std::{
    fmt::{Display, Formatter},
    result,
};

use kvm_bindings::KVM_API_VERSION;
use kvm_ioctls::Kvm;

/// Errors associated with the wrappers over KVM ioctls.
#[derive(Debug)]
pub enum Error {
    /// The host kernel reports an invalid KVM API version.
    KvmApiVersion(i32),
    /// Cannot initialize the KVM context due to missing capabilities.
    KvmCap(kvm_ioctls::Cap),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        use self::Error::*;

        match self {
            KvmApiVersion(v) => write!(
                f,
                "The host kernel reports an invalid KVM API version: {}",
                v
            ),
            KvmCap(cap) => write!(f, "Missing KVM capabilities: {:?}", cap),
        }
    }
}

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

/// Describes a KVM context that gets attached to the microVM.
/// It gives access to the functionality of the KVM wrapper as
/// long as every required KVM capability is present on the host.
pub struct KvmContext {
    kvm: Kvm,
    max_memslots: usize,
}

impl KvmContext {
    pub fn new() -> Result<Self> {
        use kvm_ioctls::Cap::*;
        let kvm = Kvm::new().expect("Error creating the Kvm object");

        // Check that KVM has the correct version.
        if kvm.get_api_version() != KVM_API_VERSION as i32 {
            return Err(Error::KvmApiVersion(kvm.get_api_version()));
        }

        // A list of KVM capabilities we want to check.
        #[cfg(target_arch = "x86_64")]
        let capabilities = vec![
            Irqchip,
            Ioeventfd,
            Irqfd,
            UserMemory,
            SetTssAddr,
            Pit2,
            PitState2,
            AdjustClock,
            Debugregs,
            MpState,
            VcpuEvents,
            Xcrs,
            Xsave,
            ExtCpuid,
        ];

        #[cfg(target_arch = "aarch64")]
        let capabilities = vec![
            Irqchip, Ioeventfd, Irqfd, UserMemory, ArmPsci02, DeviceCtrl, MpState, OneReg,
        ];

        // Check that all desired capabilities are supported.
        match capabilities
            .iter()
            .find(|&capability| !kvm.check_extension(*capability))
        {
            None => {
                let max_memslots = kvm.get_nr_memslots();
                Ok(KvmContext { kvm, max_memslots })
            }

            Some(c) => Err(Error::KvmCap(*c)),
        }
    }

    pub fn fd(&self) -> &Kvm {
        &self.kvm
    }

    /// Get the maximum number of memory slots reported by this KVM context.
    pub fn max_memslots(&self) -> usize {
        self.max_memslots
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;

    use super::*;

    #[test]
    fn test_kvm_context() {
        use std::os::unix::fs::MetadataExt;
        use std::os::unix::io::{AsRawFd, FromRawFd};

        let c = KvmContext::new().unwrap();

        assert!(c.max_memslots() >= 32);

        let kvm = Kvm::new().unwrap();
        let f = unsafe { File::from_raw_fd(kvm.as_raw_fd()) };
        let m1 = f.metadata().unwrap();
        let m2 = File::open("/dev/kvm").unwrap().metadata().unwrap();

        assert_eq!(m1.dev(), m2.dev());
        assert_eq!(m1.ino(), m2.ino());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[deny(missing_docs)]

extern crate gfx_core as core;
extern crate gfx_device_gl as device_gl;
extern crate glutin;

#[cfg(feature = "headless")]
pub use headless::Headless;

use core::{format, handle, texture};
use core::memory;
use device_gl::Resources as R;
use glutin::GlContext;
use std::rc::Rc;

mod headless;

fn get_window_dimensions(window: &glutin::GlWindow) -> texture::Dimensions {
    let (width, height) = window.get_inner_size().unwrap();
    let aa = window.get_pixel_format().multisampling
                   .unwrap_or(0) as texture::NumSamples;
    ((width as f32 * window.hidpi_factor()) as texture::Size, (height as f32 * window.hidpi_factor()) as texture::Size, 1, aa.into())
}

/*
/// Update the internal dimensions of the main framebuffer targets. Generic version over the format.
pub fn update_views<Cf, Df>(window: &glutin::GlWindow, color_view: &mut handle::RenderTargetView<R, Cf>,
                    ds_view: &mut handle::DepthStencilView<R, Df>)
where
    Cf: format::RenderFormat,
    Df: format::DepthFormat,
{
    let dim = color_view.get_dimensions();
    assert_eq!(dim, ds_view.get_dimensions());
    if let Some((cv, dv)) = update_views_raw(window, dim, Cf::get_format(), Df::get_format()) {
        *color_view = Typed::new(cv);
        *ds_view = Typed::new(dv);
    }
}

/// Return new main target views if the window resolution has changed from the old dimensions.
pub fn update_views_raw(window: &glutin::GlWindow, old_dimensions: texture::Dimensions,
                        color_format: format::Format, ds_format: format::Format)
                        -> Option<(handle::RawRenderTargetView<R>, handle::RawDepthStencilView<R>)>
{
    let dim = get_window_dimensions(window);
    if dim != old_dimensions {
        Some(device_gl::create_main_targets_raw(dim, color_format.0, ds_format.0))
    }else {
        None
    }
}
*/

pub struct SwapChain {
    // Underlying window, required for presentation
    window: Rc<glutin::Window>,
    // Single element backbuffer
    backbuffer: [core::Backbuffer<device_gl::Backend>; 1],
}

impl core::SwapChain<device_gl::Backend> for SwapChain {
    fn get_backbuffers(&mut self) -> &[core::Backbuffer<device_gl::Backend>] {
        &self.backbuffer
    }

    fn acquire_frame(&mut self, sync: core::FrameSync<device_gl::Resources>) -> core::Frame {
        // TODO: sync
        core::Frame::new(0)
    }

    fn present<Q>(&mut self, _: &mut Q, _: &[&handle::Semaphore<device_gl::Resources>])
        where Q: AsMut<device_gl::CommandQueue>
    {
        self.window.swap_buffers();
    }
}

pub struct Surface {
    window: Rc<glutin::Window>,
    manager: handle::Manager<R>,
}

impl core::Surface<device_gl::Backend> for Surface {
    type SwapChain = SwapChain;

    fn supports_queue(&self, _: &device_gl::QueueFamily) -> bool { true }
    fn build_swapchain<Q>(&mut self, config: core::SwapchainConfig, present_queue: &Q) -> SwapChain
        where Q: AsRef<device_gl::CommandQueue>
    {
        use core::handle::Producer;
        let dim = get_window_dimensions(&self.window);
        let color = self.manager.make_texture(
            device_gl::NewTexture::Surface(0),
            texture::Info {
                levels: 1,
                kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                format: config.color_format.0,
                bind: memory::RENDER_TARGET | memory::TRANSFER_SRC,
                usage: memory::Usage::Data,
            },
        );

        let ds = config.depth_stencil_format.map(|ds_format| {
            self.manager.make_texture(
                device_gl::NewTexture::Surface(0),
                texture::Info {
                    levels: 1,
                    kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                    format: ds_format.0,
                    bind: memory::DEPTH_STENCIL | memory::TRANSFER_SRC,
                    usage: memory::Usage::Data,
                },
            )
        });

        SwapChain {
            window: self.window.clone(),
            backbuffer: [(color, ds); 1],
        }
    }
}

pub struct Window(Rc<glutin::Window>);

pub fn build(builder: glutin::WindowBuilder, events_loop: &glutin::EventsLoop,
             color_format: format::Format, ds_format: format::Format) -> glutin::Window {
    let color_total_bits = color_format.0.get_total_bits();
    let alpha_bits = color_format.0.get_alpha_stencil_bits();
    let depth_total_bits = ds_format.0.get_total_bits();
    let stencil_bits = ds_format.0.get_alpha_stencil_bits();
    builder
        .with_depth_buffer(depth_total_bits - stencil_bits)
        .with_stencil_buffer(stencil_bits)
        .with_pixel_format(color_total_bits - alpha_bits, alpha_bits)
        .with_srgb(Some(color_format.1 == format::ChannelType::Srgb))
        .build(events_loop)
        .unwrap()
}

impl Window {
    /// Create a new window.
    pub fn new(window: glutin::Window) -> Self {
        Window(Rc::new(window))
    }

    /// Get the internal glutin window.
    pub fn raw(&self) -> &glutin::Window {
        &self.0
    }
}
impl core::WindowExt<device_gl::Backend> for Window {
    type Surface = Surface;
    type Adapter = device_gl::Adapter;

    fn get_surface_and_adapters(&mut self) -> (Surface, Vec<device_gl::Adapter>) {
        unsafe { self.0.make_current().unwrap() };
        let adapter = device_gl::Adapter::new(|s| self.0.get_proc_address(s) as *const std::os::raw::c_void);
        let surface = Surface {
            window: self.0.clone(),
            manager: handle::Manager::new(),
        };

        (surface, vec![adapter])
    }
}
<commit_msg>[ll] Feature gate glutin headless for travis<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[deny(missing_docs)]

extern crate gfx_core as core;
extern crate gfx_device_gl as device_gl;
extern crate glutin;

#[cfg(feature = "headless")]
pub use headless::Headless;

use core::{format, handle, texture};
use core::memory;
use device_gl::Resources as R;
use glutin::GlContext;
use std::rc::Rc;

#[cfg(feature = "headless")]
mod headless;

fn get_window_dimensions(window: &glutin::GlWindow) -> texture::Dimensions {
    let (width, height) = window.get_inner_size().unwrap();
    let aa = window.get_pixel_format().multisampling
                   .unwrap_or(0) as texture::NumSamples;
    ((width as f32 * window.hidpi_factor()) as texture::Size, (height as f32 * window.hidpi_factor()) as texture::Size, 1, aa.into())
}

/*
/// Update the internal dimensions of the main framebuffer targets. Generic version over the format.
pub fn update_views<Cf, Df>(window: &glutin::GlWindow, color_view: &mut handle::RenderTargetView<R, Cf>,
                    ds_view: &mut handle::DepthStencilView<R, Df>)
where
    Cf: format::RenderFormat,
    Df: format::DepthFormat,
{
    let dim = color_view.get_dimensions();
    assert_eq!(dim, ds_view.get_dimensions());
    if let Some((cv, dv)) = update_views_raw(window, dim, Cf::get_format(), Df::get_format()) {
        *color_view = Typed::new(cv);
        *ds_view = Typed::new(dv);
    }
}

/// Return new main target views if the window resolution has changed from the old dimensions.
pub fn update_views_raw(window: &glutin::GlWindow, old_dimensions: texture::Dimensions,
                        color_format: format::Format, ds_format: format::Format)
                        -> Option<(handle::RawRenderTargetView<R>, handle::RawDepthStencilView<R>)>
{
    let dim = get_window_dimensions(window);
    if dim != old_dimensions {
        Some(device_gl::create_main_targets_raw(dim, color_format.0, ds_format.0))
    }else {
        None
    }
}
*/

pub struct SwapChain {
    // Underlying window, required for presentation
    window: Rc<glutin::Window>,
    // Single element backbuffer
    backbuffer: [core::Backbuffer<device_gl::Backend>; 1],
}

impl core::SwapChain<device_gl::Backend> for SwapChain {
    fn get_backbuffers(&mut self) -> &[core::Backbuffer<device_gl::Backend>] {
        &self.backbuffer
    }

    fn acquire_frame(&mut self, sync: core::FrameSync<device_gl::Resources>) -> core::Frame {
        // TODO: sync
        core::Frame::new(0)
    }

    fn present<Q>(&mut self, _: &mut Q, _: &[&handle::Semaphore<device_gl::Resources>])
        where Q: AsMut<device_gl::CommandQueue>
    {
        self.window.swap_buffers();
    }
}

pub struct Surface {
    window: Rc<glutin::Window>,
    manager: handle::Manager<R>,
}

impl core::Surface<device_gl::Backend> for Surface {
    type SwapChain = SwapChain;

    fn supports_queue(&self, _: &device_gl::QueueFamily) -> bool { true }
    fn build_swapchain<Q>(&mut self, config: core::SwapchainConfig, present_queue: &Q) -> SwapChain
        where Q: AsRef<device_gl::CommandQueue>
    {
        use core::handle::Producer;
        let dim = get_window_dimensions(&self.window);
        let color = self.manager.make_texture(
            device_gl::NewTexture::Surface(0),
            texture::Info {
                levels: 1,
                kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                format: config.color_format.0,
                bind: memory::RENDER_TARGET | memory::TRANSFER_SRC,
                usage: memory::Usage::Data,
            },
        );

        let ds = config.depth_stencil_format.map(|ds_format| {
            self.manager.make_texture(
                device_gl::NewTexture::Surface(0),
                texture::Info {
                    levels: 1,
                    kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                    format: ds_format.0,
                    bind: memory::DEPTH_STENCIL | memory::TRANSFER_SRC,
                    usage: memory::Usage::Data,
                },
            )
        });

        SwapChain {
            window: self.window.clone(),
            backbuffer: [(color, ds); 1],
        }
    }
}

pub struct Window(Rc<glutin::Window>);

pub fn build(builder: glutin::WindowBuilder, events_loop: &glutin::EventsLoop,
             color_format: format::Format, ds_format: format::Format) -> glutin::Window {
    let color_total_bits = color_format.0.get_total_bits();
    let alpha_bits = color_format.0.get_alpha_stencil_bits();
    let depth_total_bits = ds_format.0.get_total_bits();
    let stencil_bits = ds_format.0.get_alpha_stencil_bits();
    builder
        .with_depth_buffer(depth_total_bits - stencil_bits)
        .with_stencil_buffer(stencil_bits)
        .with_pixel_format(color_total_bits - alpha_bits, alpha_bits)
        .with_srgb(Some(color_format.1 == format::ChannelType::Srgb))
        .build(events_loop)
        .unwrap()
}

impl Window {
    /// Create a new window.
    pub fn new(window: glutin::Window) -> Self {
        Window(Rc::new(window))
    }

    /// Get the internal glutin window.
    pub fn raw(&self) -> &glutin::Window {
        &self.0
    }
}
impl core::WindowExt<device_gl::Backend> for Window {
    type Surface = Surface;
    type Adapter = device_gl::Adapter;

    fn get_surface_and_adapters(&mut self) -> (Surface, Vec<device_gl::Adapter>) {
        unsafe { self.0.make_current().unwrap() };
        let adapter = device_gl::Adapter::new(|s| self.0.get_proc_address(s) as *const std::os::raw::c_void);
        let surface = Surface {
            window: self.0.clone(),
            manager: handle::Manager::new(),
        };

        (surface, vec![adapter])
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[deny(missing_docs)]

extern crate gfx_core as core;
extern crate gfx_device_gl as device_gl;
extern crate glutin;

pub use headless::Headless;

use core::{format, handle, texture};
use core::memory;
use device_gl::Resources as R;
use glutin::GlContext;
use std::rc::Rc;

mod headless;

fn get_window_dimensions(window: &glutin::GlWindow) -> texture::Dimensions {
    let (width, height) = window.get_inner_size().unwrap();
    let aa = window.get_pixel_format().multisampling
                   .unwrap_or(0) as texture::NumSamples;
    ((width as f32 * window.hidpi_factor()) as texture::Size, (height as f32 * window.hidpi_factor()) as texture::Size, 1, aa.into())
}

/*
/// Update the internal dimensions of the main framebuffer targets. Generic version over the format.
pub fn update_views<Cf, Df>(window: &glutin::GlWindow, color_view: &mut handle::RenderTargetView<R, Cf>,
                    ds_view: &mut handle::DepthStencilView<R, Df>)
where
    Cf: format::RenderFormat,
    Df: format::DepthFormat,
{
    let dim = color_view.get_dimensions();
    assert_eq!(dim, ds_view.get_dimensions());
    if let Some((cv, dv)) = update_views_raw(window, dim, Cf::get_format(), Df::get_format()) {
        *color_view = Typed::new(cv);
        *ds_view = Typed::new(dv);
    }
}

/// Return new main target views if the window resolution has changed from the old dimensions.
pub fn update_views_raw(window: &glutin::GlWindow, old_dimensions: texture::Dimensions,
                        color_format: format::Format, ds_format: format::Format)
                        -> Option<(handle::RawRenderTargetView<R>, handle::RawDepthStencilView<R>)>
{
    let dim = get_window_dimensions(window);
    if dim != old_dimensions {
        Some(device_gl::create_main_targets_raw(dim, color_format.0, ds_format.0))
    }else {
        None
    }
}
*/

pub struct SwapChain {
    // Underlying window, required for presentation
    window: Rc<glutin::Window>,
    // Single element backbuffer
    backbuffer: [core::Backbuffer<device_gl::Backend>; 1],
}

impl core::SwapChain<device_gl::Backend> for SwapChain {
    fn get_backbuffers(&mut self) -> &[core::Backbuffer<device_gl::Backend>] {
        &self.backbuffer
    }

    fn acquire_frame(&mut self, sync: core::FrameSync<device_gl::Resources>) -> core::Frame {
        // TODO: sync
        core::Frame::new(0)
    }

    fn present<Q>(&mut self, _: &mut Q, _: &[&handle::Semaphore<device_gl::Resources>])
        where Q: AsMut<device_gl::CommandQueue>
    {
        self.window.swap_buffers();
    }
}

pub struct Surface {
    window: Rc<glutin::Window>,
    manager: handle::Manager<R>,
}

impl core::Surface<device_gl::Backend> for Surface {
    type SwapChain = SwapChain;

    fn supports_queue(&self, _: &device_gl::QueueFamily) -> bool { true }
    fn build_swapchain<Q>(&mut self, config: core::SwapchainConfig, present_queue: &Q) -> SwapChain
        where Q: AsRef<device_gl::CommandQueue>
    {
        use core::handle::Producer;
        let dim = get_window_dimensions(&self.window);
        let color = self.manager.make_texture(
            device_gl::NewTexture::Surface(0),
            texture::Info {
                levels: 1,
                kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                format: config.color_format.0,
                bind: memory::RENDER_TARGET | memory::TRANSFER_SRC,
                usage: memory::Usage::Data,
            },
        );

        let ds = config.depth_stencil_format.map(|ds_format| {
            self.manager.make_texture(
                device_gl::NewTexture::Surface(0),
                texture::Info {
                    levels: 1,
                    kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                    format: ds_format.0,
                    bind: memory::DEPTH_STENCIL | memory::TRANSFER_SRC,
                    usage: memory::Usage::Data,
                },
            )
        });

        SwapChain {
            window: self.window.clone(),
            backbuffer: [(color, ds); 1],
        }
    }
}

pub struct Window(Rc<glutin::Window>);

pub fn build(builder: glutin::WindowBuilder, events_loop: &glutin::EventsLoop,
             color_format: format::Format, ds_format: format::Format) -> glutin::Window {
    let color_total_bits = color_format.0.get_total_bits();
    let alpha_bits = color_format.0.get_alpha_stencil_bits();
    let depth_total_bits = ds_format.0.get_total_bits();
    let stencil_bits = ds_format.0.get_alpha_stencil_bits();
    builder
        .with_depth_buffer(depth_total_bits - stencil_bits)
        .with_stencil_buffer(stencil_bits)
        .with_pixel_format(color_total_bits - alpha_bits, alpha_bits)
        .with_srgb(Some(color_format.1 == format::ChannelType::Srgb))
        .build(events_loop)
        .unwrap()
}

impl Window {
    /// Create a new window.
    pub fn new(window: glutin::Window) -> Self {
        Window(Rc::new(window))
    }

    /// Get the internal glutin window.
    pub fn raw(&self) -> &glutin::Window {
        &self.0
    }
}
impl core::WindowExt<device_gl::Backend> for Window {
    type Surface = Surface;
    type Adapter = device_gl::Adapter;

    fn get_surface_and_adapters(&mut self) -> (Surface, Vec<device_gl::Adapter>) {
        unsafe { self.0.make_current().unwrap() };
        let adapter = device_gl::Adapter::new(|s| self.0.get_proc_address(s) as *const std::os::raw::c_void);
        let surface = Surface {
            window: self.0.clone(),
            manager: handle::Manager::new(),
        };

        (surface, vec![adapter])
    }
}
<commit_msg>[ll] Feature gate glutin headless for travis<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[deny(missing_docs)]

extern crate gfx_core as core;
extern crate gfx_device_gl as device_gl;
extern crate glutin;

#[cfg(feature = "headless")]
pub use headless::Headless;

use core::{format, handle, texture};
use core::memory;
use device_gl::Resources as R;
use glutin::GlContext;
use std::rc::Rc;

#[cfg(feature = "headless")]
mod headless;

fn get_window_dimensions(window: &glutin::GlWindow) -> texture::Dimensions {
    let (width, height) = window.get_inner_size().unwrap();
    let aa = window.get_pixel_format().multisampling
                   .unwrap_or(0) as texture::NumSamples;
    ((width as f32 * window.hidpi_factor()) as texture::Size, (height as f32 * window.hidpi_factor()) as texture::Size, 1, aa.into())
}

/*
/// Update the internal dimensions of the main framebuffer targets. Generic version over the format.
pub fn update_views<Cf, Df>(window: &glutin::GlWindow, color_view: &mut handle::RenderTargetView<R, Cf>,
                    ds_view: &mut handle::DepthStencilView<R, Df>)
where
    Cf: format::RenderFormat,
    Df: format::DepthFormat,
{
    let dim = color_view.get_dimensions();
    assert_eq!(dim, ds_view.get_dimensions());
    if let Some((cv, dv)) = update_views_raw(window, dim, Cf::get_format(), Df::get_format()) {
        *color_view = Typed::new(cv);
        *ds_view = Typed::new(dv);
    }
}

/// Return new main target views if the window resolution has changed from the old dimensions.
pub fn update_views_raw(window: &glutin::GlWindow, old_dimensions: texture::Dimensions,
                        color_format: format::Format, ds_format: format::Format)
                        -> Option<(handle::RawRenderTargetView<R>, handle::RawDepthStencilView<R>)>
{
    let dim = get_window_dimensions(window);
    if dim != old_dimensions {
        Some(device_gl::create_main_targets_raw(dim, color_format.0, ds_format.0))
    }else {
        None
    }
}
*/

pub struct SwapChain {
    // Underlying window, required for presentation
    window: Rc<glutin::Window>,
    // Single element backbuffer
    backbuffer: [core::Backbuffer<device_gl::Backend>; 1],
}

impl core::SwapChain<device_gl::Backend> for SwapChain {
    fn get_backbuffers(&mut self) -> &[core::Backbuffer<device_gl::Backend>] {
        &self.backbuffer
    }

    fn acquire_frame(&mut self, sync: core::FrameSync<device_gl::Resources>) -> core::Frame {
        // TODO: sync
        core::Frame::new(0)
    }

    fn present<Q>(&mut self, _: &mut Q, _: &[&handle::Semaphore<device_gl::Resources>])
        where Q: AsMut<device_gl::CommandQueue>
    {
        self.window.swap_buffers();
    }
}

pub struct Surface {
    window: Rc<glutin::Window>,
    manager: handle::Manager<R>,
}

impl core::Surface<device_gl::Backend> for Surface {
    type SwapChain = SwapChain;

    fn supports_queue(&self, _: &device_gl::QueueFamily) -> bool { true }
    fn build_swapchain<Q>(&mut self, config: core::SwapchainConfig, present_queue: &Q) -> SwapChain
        where Q: AsRef<device_gl::CommandQueue>
    {
        use core::handle::Producer;
        let dim = get_window_dimensions(&self.window);
        let color = self.manager.make_texture(
            device_gl::NewTexture::Surface(0),
            texture::Info {
                levels: 1,
                kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                format: config.color_format.0,
                bind: memory::RENDER_TARGET | memory::TRANSFER_SRC,
                usage: memory::Usage::Data,
            },
        );

        let ds = config.depth_stencil_format.map(|ds_format| {
            self.manager.make_texture(
                device_gl::NewTexture::Surface(0),
                texture::Info {
                    levels: 1,
                    kind: texture::Kind::D2(dim.0, dim.1, dim.3),
                    format: ds_format.0,
                    bind: memory::DEPTH_STENCIL | memory::TRANSFER_SRC,
                    usage: memory::Usage::Data,
                },
            )
        });

        SwapChain {
            window: self.window.clone(),
            backbuffer: [(color, ds); 1],
        }
    }
}

pub struct Window(Rc<glutin::Window>);

pub fn build(builder: glutin::WindowBuilder, events_loop: &glutin::EventsLoop,
             color_format: format::Format, ds_format: format::Format) -> glutin::Window {
    let color_total_bits = color_format.0.get_total_bits();
    let alpha_bits = color_format.0.get_alpha_stencil_bits();
    let depth_total_bits = ds_format.0.get_total_bits();
    let stencil_bits = ds_format.0.get_alpha_stencil_bits();
    builder
        .with_depth_buffer(depth_total_bits - stencil_bits)
        .with_stencil_buffer(stencil_bits)
        .with_pixel_format(color_total_bits - alpha_bits, alpha_bits)
        .with_srgb(Some(color_format.1 == format::ChannelType::Srgb))
        .build(events_loop)
        .unwrap()
}

impl Window {
    /// Create a new window.
    pub fn new(window: glutin::Window) -> Self {
        Window(Rc::new(window))
    }

    /// Get the internal glutin window.
    pub fn raw(&self) -> &glutin::Window {
        &self.0
    }
}
impl core::WindowExt<device_gl::Backend> for Window {
    type Surface = Surface;
    type Adapter = device_gl::Adapter;

    fn get_surface_and_adapters(&mut self) -> (Surface, Vec<device_gl::Adapter>) {
        unsafe { self.0.make_current().unwrap() };
        let adapter = device_gl::Adapter::new(|s| self.0.get_proc_address(s) as *const std::os::raw::c_void);
        let surface = Surface {
            window: self.0.clone(),
            manager: handle::Manager::new(),
        };

        (surface, vec![adapter])
    }
}
<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast;
use syntax::source_map::Span;
use syntax::visit::FnKind;
use syntax_pos::BytePos;

use crate::utils::{match_path_ast, snippet_opt, span_lint_and_then};

declare_clippy_lint! {
    /// **What it does:** Checks for return statements at the end of a block.
    ///
    /// **Why is this bad?** Removing the `return` and semicolon will make the code
    /// more rusty.
    ///
    /// **Known problems:** If the computation returning the value borrows a local
    /// variable, removing the `return` may run afoul of the borrow checker.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    /// simplify to
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    pub NEEDLESS_RETURN,
    style,
    "using a return statement like `return expr;` where an expression would suffice"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let`-bindings, which are subsequently
    /// returned.
    ///
    /// **Why is this bad?** It is just extraneous code. Remove it to make your code
    /// more rusty.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo() -> String {
    ///     let x = String::new();
    ///     x
    /// }
    /// ```
    /// instead, use
    /// ```
    /// fn foo() -> String {
    ///     String::new()
    /// }
    /// ```
    pub LET_AND_RETURN,
    style,
    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
}

declare_clippy_lint! {
    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
    ///
    /// **Why is this bad?** Such expressions add no value, but can make the code
    /// less readable. Depending on formatting they can make a `break` or `return`
    /// statement look like a function call.
    ///
    /// **Known problems:** The lint currently misses unit return types in types,
    /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
    ///
    /// **Example:**
    /// ```rust
    /// fn return_unit() -> () {
    ///     ()
    /// }
    /// ```
    pub UNUSED_UNIT,
    style,
    "needless unit expression"
}

#[derive(PartialEq, Eq, Copy, Clone)]
enum RetReplacement {
    Empty,
    Block,
}

declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);

impl Return {
    // Check the final stmt or expr in a block for unnecessary return.
    fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        if let Some(stmt) = block.stmts.last() {
            match stmt.node {
                ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
                    self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
                },
                _ => (),
            }
        }
    }

    // Check a the final expression in a block if it's a return.
    fn check_final_expr(
        &mut self,
        cx: &EarlyContext<'_>,
        expr: &ast::Expr,
        span: Option<Span>,
        replacement: RetReplacement,
    ) {
        match expr.node {
            // simple return is always "bad"
            ast::ExprKind::Ret(ref inner) => {
                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
                if !expr.attrs.iter().any(attr_is_cfg) {
                    self.emit_return_lint(
                        cx,
                        span.expect("`else return` is not possible"),
                        inner.as_ref().map(|i| i.span),
                        replacement,
                    );
                }
            },
            // a whole block? check it!
            ast::ExprKind::Block(ref block, _) => {
                self.check_block_return(cx, block);
            },
            // an if/if let expr, check both exprs
            // note, if without else is going to be a type checking error anyways
            // (except for unit type functions) so we don't match it
            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
                self.check_block_return(cx, ifblock);
                self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
            },
            // a match expr, check all arms
            ast::ExprKind::Match(_, ref arms) => {
                for arm in arms {
                    self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
                }
            },
            _ => (),
        }
    }

    fn emit_return_lint(
        &mut self,
        cx: &EarlyContext<'_>,
        ret_span: Span,
        inner_span: Option<Span>,
        replacement: RetReplacement,
    ) {
        match inner_span {
            Some(inner_span) => {
                if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
                    return;
                }

                span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                    if let Some(snippet) = snippet_opt(cx, inner_span) {
                        db.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
                    }
                })
            },
            None => match replacement {
                RetReplacement::Empty => {
                    span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                        db.span_suggestion(
                            ret_span,
                            "remove `return`",
                            String::new(),
                            Applicability::MachineApplicable,
                        );
                    });
                },
                RetReplacement::Block => {
                    span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                        db.span_suggestion(
                            ret_span,
                            "replace `return` with an empty block",
                            "{}".to_string(),
                            Applicability::MachineApplicable,
                        );
                    });
                },
            },
        }
    }

    // Check for "let x = EXPR; x"
    fn check_let_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        let mut it = block.stmts.iter();

        // we need both a let-binding stmt and an expr
        if_chain! {
            if let Some(retexpr) = it.next_back();
            if let ast::StmtKind::Expr(ref retexpr) = retexpr.node;
            if let Some(stmt) = it.next_back();
            if let ast::StmtKind::Local(ref local) = stmt.node;
            // don't lint in the presence of type inference
            if local.ty.is_none();
            if local.attrs.is_empty();
            if let Some(ref initexpr) = local.init;
            if let ast::PatKind::Ident(_, ident, _) = local.pat.node;
            if let ast::ExprKind::Path(_, ref path) = retexpr.node;
            if match_path_ast(path, &[&*ident.name.as_str()]);
            if !in_external_macro(cx.sess(), initexpr.span);
            then {
                span_lint_and_then(
                    cx,
                    LET_AND_RETURN,
                    retexpr.span,
                    "returning the result of a let binding from a block",
                    |err| {
                        err.span_label(local.span, "unnecessary let binding");

                        if let Some(snippet) = snippet_opt(cx, initexpr.span) {
                            err.multipart_suggestion(
                                "return the expression directly",
                                vec![
                                    (local.span, String::new()),
                                    (retexpr.span, snippet),
                                ],
                                Applicability::MachineApplicable,
                            );
                        } else {
                            err.span_help(initexpr.span, "this expression can be directly returned");
                        }
                    },
                );
            }
        }
    }
}

impl EarlyLintPass for Return {
    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, decl: &ast::FnDecl, span: Span, _: ast::NodeId) {
        match kind {
            FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block),
            FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
        }
        if_chain! {
            if let ast::FunctionRetTy::Ty(ref ty) = decl.output;
            if let ast::TyKind::Tup(ref vals) = ty.node;
            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
            then {
                let (rspan, appl) = if let Ok(fn_source) =
                        cx.sess().source_map()
                                 .span_to_snippet(span.with_hi(ty.span.hi())) {
                    if let Some(rpos) = fn_source.rfind("->") {
                        #[allow(clippy::cast_possible_truncation)]
                        (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
                            Applicability::MachineApplicable)
                    } else {
                        (ty.span, Applicability::MaybeIncorrect)
                    }
                } else {
                    (ty.span, Applicability::MaybeIncorrect)
                };
                span_lint_and_then(cx, UNUSED_UNIT, rspan, "unneeded unit return type", |db| {
                    db.span_suggestion(
                        rspan,
                        "remove the `-> ()`",
                        String::new(),
                        appl,
                    );
                });
            }
        }
    }

    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        self.check_let_return(cx, block);
        if_chain! {
            if let Some(ref stmt) = block.stmts.last();
            if let ast::StmtKind::Expr(ref expr) = stmt.node;
            if is_unit_expr(expr) && !stmt.span.from_expansion();
            then {
                let sp = expr.span;
                span_lint_and_then(cx, UNUSED_UNIT, sp, "unneeded unit expression", |db| {
                    db.span_suggestion(
                        sp,
                        "remove the final `()`",
                        String::new(),
                        Applicability::MachineApplicable,
                    );
                });
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
        match e.node {
            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
                if is_unit_expr(expr) && !expr.span.from_expansion() {
                    span_lint_and_then(cx, UNUSED_UNIT, expr.span, "unneeded `()`", |db| {
                        db.span_suggestion(
                            expr.span,
                            "remove the `()`",
                            String::new(),
                            Applicability::MachineApplicable,
                        );
                    });
                }
            },
            _ => (),
        }
    }
}

fn attr_is_cfg(attr: &ast::Attribute) -> bool {
    attr.meta_item_list().is_some() && attr.check_name(sym!(cfg))
}

// get the def site
fn get_def(span: Span) -> Option<Span> {
    if span.from_expansion() {
        Some(span.ctxt().outer_expn_data().def_site)
    } else {
        None
    }
}

// is this expr a `()` unit?
fn is_unit_expr(expr: &ast::Expr) -> bool {
    if let ast::ExprKind::Tup(ref vals) = expr.node {
        vals.is_empty()
    } else {
        false
    }
}
<commit_msg>Auto merge of #4561 - rust-lang:let-return-fix, r=phansch<commit_after>use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast;
use syntax::source_map::Span;
use syntax::visit::FnKind;
use syntax_pos::BytePos;

use crate::utils::{match_path_ast, snippet_opt, span_lint_and_then};

declare_clippy_lint! {
    /// **What it does:** Checks for return statements at the end of a block.
    ///
    /// **Why is this bad?** Removing the `return` and semicolon will make the code
    /// more rusty.
    ///
    /// **Known problems:** If the computation returning the value borrows a local
    /// variable, removing the `return` may run afoul of the borrow checker.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     return x;
    /// }
    /// ```
    /// simplify to
    /// ```rust
    /// fn foo(x: usize) -> usize {
    ///     x
    /// }
    /// ```
    pub NEEDLESS_RETURN,
    style,
    "using a return statement like `return expr;` where an expression would suffice"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let`-bindings, which are subsequently
    /// returned.
    ///
    /// **Why is this bad?** It is just extraneous code. Remove it to make your code
    /// more rusty.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo() -> String {
    ///     let x = String::new();
    ///     x
    /// }
    /// ```
    /// instead, use
    /// ```
    /// fn foo() -> String {
    ///     String::new()
    /// }
    /// ```
    pub LET_AND_RETURN,
    style,
    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
}

declare_clippy_lint! {
    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
    ///
    /// **Why is this bad?** Such expressions add no value, but can make the code
    /// less readable. Depending on formatting they can make a `break` or `return`
    /// statement look like a function call.
    ///
    /// **Known problems:** The lint currently misses unit return types in types,
    /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
    ///
    /// **Example:**
    /// ```rust
    /// fn return_unit() -> () {
    ///     ()
    /// }
    /// ```
    pub UNUSED_UNIT,
    style,
    "needless unit expression"
}

#[derive(PartialEq, Eq, Copy, Clone)]
enum RetReplacement {
    Empty,
    Block,
}

declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);

impl Return {
    // Check the final stmt or expr in a block for unnecessary return.
    fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        if let Some(stmt) = block.stmts.last() {
            match stmt.node {
                ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
                    self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
                },
                _ => (),
            }
        }
    }

    // Check a the final expression in a block if it's a return.
    fn check_final_expr(
        &mut self,
        cx: &EarlyContext<'_>,
        expr: &ast::Expr,
        span: Option<Span>,
        replacement: RetReplacement,
    ) {
        match expr.node {
            // simple return is always "bad"
            ast::ExprKind::Ret(ref inner) => {
                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
                if !expr.attrs.iter().any(attr_is_cfg) {
                    self.emit_return_lint(
                        cx,
                        span.expect("`else return` is not possible"),
                        inner.as_ref().map(|i| i.span),
                        replacement,
                    );
                }
            },
            // a whole block? check it!
            ast::ExprKind::Block(ref block, _) => {
                self.check_block_return(cx, block);
            },
            // an if/if let expr, check both exprs
            // note, if without else is going to be a type checking error anyways
            // (except for unit type functions) so we don't match it
            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
                self.check_block_return(cx, ifblock);
                self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
            },
            // a match expr, check all arms
            ast::ExprKind::Match(_, ref arms) => {
                for arm in arms {
                    self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
                }
            },
            _ => (),
        }
    }

    fn emit_return_lint(
        &mut self,
        cx: &EarlyContext<'_>,
        ret_span: Span,
        inner_span: Option<Span>,
        replacement: RetReplacement,
    ) {
        match inner_span {
            Some(inner_span) => {
                if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
                    return;
                }

                span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                    if let Some(snippet) = snippet_opt(cx, inner_span) {
                        db.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
                    }
                })
            },
            None => match replacement {
                RetReplacement::Empty => {
                    span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                        db.span_suggestion(
                            ret_span,
                            "remove `return`",
                            String::new(),
                            Applicability::MachineApplicable,
                        );
                    });
                },
                RetReplacement::Block => {
                    span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
                        db.span_suggestion(
                            ret_span,
                            "replace `return` with an empty block",
                            "{}".to_string(),
                            Applicability::MachineApplicable,
                        );
                    });
                },
            },
        }
    }

    // Check for "let x = EXPR; x"
    fn check_let_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        let mut it = block.stmts.iter();

        // we need both a let-binding stmt and an expr
        if_chain! {
            if let Some(retexpr) = it.next_back();
            if let ast::StmtKind::Expr(ref retexpr) = retexpr.node;
            if let Some(stmt) = it.next_back();
            if let ast::StmtKind::Local(ref local) = stmt.node;
            // don't lint in the presence of type inference
            if local.ty.is_none();
            if local.attrs.is_empty();
            if let Some(ref initexpr) = local.init;
            if let ast::PatKind::Ident(_, ident, _) = local.pat.node;
            if let ast::ExprKind::Path(_, ref path) = retexpr.node;
            if match_path_ast(path, &[&*ident.name.as_str()]);
            if !in_external_macro(cx.sess(), initexpr.span);
            if !in_external_macro(cx.sess(), retexpr.span);
            if !in_external_macro(cx.sess(), local.span);
            then {
                span_lint_and_then(
                    cx,
                    LET_AND_RETURN,
                    retexpr.span,
                    "returning the result of a let binding from a block",
                    |err| {
                        err.span_label(local.span, "unnecessary let binding");

                        if let Some(snippet) = snippet_opt(cx, initexpr.span) {
                            err.multipart_suggestion(
                                "return the expression directly",
                                vec![
                                    (local.span, String::new()),
                                    (retexpr.span, snippet),
                                ],
                                Applicability::MachineApplicable,
                            );
                        } else {
                            err.span_help(initexpr.span, "this expression can be directly returned");
                        }
                    },
                );
            }
        }
    }
}

impl EarlyLintPass for Return {
    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, decl: &ast::FnDecl, span: Span, _: ast::NodeId) {
        match kind {
            FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block),
            FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
        }
        if_chain! {
            if let ast::FunctionRetTy::Ty(ref ty) = decl.output;
            if let ast::TyKind::Tup(ref vals) = ty.node;
            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
            then {
                let (rspan, appl) = if let Ok(fn_source) =
                        cx.sess().source_map()
                                 .span_to_snippet(span.with_hi(ty.span.hi())) {
                    if let Some(rpos) = fn_source.rfind("->") {
                        #[allow(clippy::cast_possible_truncation)]
                        (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
                            Applicability::MachineApplicable)
                    } else {
                        (ty.span, Applicability::MaybeIncorrect)
                    }
                } else {
                    (ty.span, Applicability::MaybeIncorrect)
                };
                span_lint_and_then(cx, UNUSED_UNIT, rspan, "unneeded unit return type", |db| {
                    db.span_suggestion(
                        rspan,
                        "remove the `-> ()`",
                        String::new(),
                        appl,
                    );
                });
            }
        }
    }

    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        self.check_let_return(cx, block);
        if_chain! {
            if let Some(ref stmt) = block.stmts.last();
            if let ast::StmtKind::Expr(ref expr) = stmt.node;
            if is_unit_expr(expr) && !stmt.span.from_expansion();
            then {
                let sp = expr.span;
                span_lint_and_then(cx, UNUSED_UNIT, sp, "unneeded unit expression", |db| {
                    db.span_suggestion(
                        sp,
                        "remove the final `()`",
                        String::new(),
                        Applicability::MachineApplicable,
                    );
                });
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
        match e.node {
            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
                if is_unit_expr(expr) && !expr.span.from_expansion() {
                    span_lint_and_then(cx, UNUSED_UNIT, expr.span, "unneeded `()`", |db| {
                        db.span_suggestion(
                            expr.span,
                            "remove the `()`",
                            String::new(),
                            Applicability::MachineApplicable,
                        );
                    });
                }
            },
            _ => (),
        }
    }
}

fn attr_is_cfg(attr: &ast::Attribute) -> bool {
    attr.meta_item_list().is_some() && attr.check_name(sym!(cfg))
}

// get the def site
fn get_def(span: Span) -> Option<Span> {
    if span.from_expansion() {
        Some(span.ctxt().outer_expn_data().def_site)
    } else {
        None
    }
}

// is this expr a `()` unit?
fn is_unit_expr(expr: &ast::Expr) -> bool {
    if let ast::ExprKind::Tup(ref vals) = expr.node {
        vals.is_empty()
    } else {
        false
    }
}
<|endoftext|>


<commit_before>use std::io::Read;

use oauthcli::SignatureMethod::HmacSha1;
use oauthcli::OAuthAuthorizationHeaderBuilder;
use serde_json;
use serde_json::Value;
use reqwest::{Client, Url, StatusCode, Response};
use reqwest::header::{Authorization};

const API_URL: &'static str = "https://api.twitter.com/1.1/statuses/update.json";

pub struct TwitterClient {
	consumer_key: String,
	consumer_secret: String,
	access_key: String,
	access_secret: String,
	client: Client,
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum TwitterError {
	Duplicated,
	RateLimitExceeded,
	InvalidTweet,
	InvalidResponse,
	Network,
	Unauthorized,
	Unknown(String),
}

impl TwitterClient {

	pub fn new(consumer_key: String, consumer_secret: String, access_key: String, access_secret: String) -> TwitterClient
	{
		TwitterClient {
			consumer_key: consumer_key,
			consumer_secret: consumer_secret,
			access_key: access_key,
			access_secret: access_secret,
			client: Client::new(),
		}
	}

	pub fn is_valid(&self) -> bool
	{
		// TODO: implement token validation
		true
	}

	fn post(&self, url: &str, args: Vec<(&str, &str)>) -> Result<Response, ()>
	{
		let mut req = self.client.post(url);

		req.form(&args);

		let oauth_header = OAuthAuthorizationHeaderBuilder::new(
			"POST",
			&Url::parse(url).expect("must be a valid url string"),
			self.consumer_key.as_ref(),
			self.consumer_secret.as_ref(),
			HmacSha1)
			.token(self.access_key.as_ref(), self.access_secret.as_ref())
			.request_parameters(args.into_iter())
			.finish_for_twitter()
			.to_string();

		req.header(Authorization(oauth_header)).send().map_err(|_| ())
	}

	pub fn update_status(&self, message: &str, in_reply_to: Option<u64>)
	 -> Result<u64, TwitterError>
	{
		let prev = in_reply_to.map(|i| i.to_string());
		let mut args = vec![("status", message)];

		if let Some(prev) = prev.as_ref() {
			args.push(("in_reply_to_status_id", prev));
		}

		let mut response = self.post(API_URL, args).map_err(|_| TwitterError::Network)?;

		match response.status() {

			StatusCode::Forbidden => {

				let mut body = String::new();
				response.read_to_string(&mut body).map_err(|_| TwitterError::Network)?;

				let json: Option<Value> = serde_json::from_str(&body).ok();
				let id = json.and_then(|json| json["errors"][0]["code"].as_i64());

				match id {
					Some(186) => Err(TwitterError::InvalidTweet),
					Some(187) => Err(TwitterError::Duplicated),
					_ => Err(TwitterError::Unknown(body))
				}
			},

			StatusCode::Ok => {

				let mut body = String::new();
				response.read_to_string(&mut body).map_err(|_| TwitterError::Network)?;

				let json: Value =
					serde_json::from_str(&body).map_err(|_| TwitterError::InvalidResponse)?;
				let id = json["id"].as_u64().ok_or(TwitterError::InvalidResponse)?;

				Ok(id)
			},

			StatusCode::TooManyRequests => Err(TwitterError::RateLimitExceeded),
			StatusCode::Unauthorized => Err(TwitterError::Unauthorized),
			_ => Err(TwitterError::Unknown(format!("unknown status: {}", response.status())))
		}
	}
}
<commit_msg>Fix build error on 1.28<commit_after>use std::io::Read;

use oauthcli::SignatureMethod::HmacSha1;
use oauthcli::OAuthAuthorizationHeaderBuilder;
use serde_json;
use serde_json::Value;
use reqwest::{Client, Url, StatusCode, Response};
use reqwest::header::{Authorization};

const API_URL: &'static str = "https://api.twitter.com/1.1/statuses/update.json";

pub struct TwitterClient {
	consumer_key: String,
	consumer_secret: String,
	access_key: String,
	access_secret: String,
	client: Client,
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum TwitterError {
	Duplicated,
	RateLimitExceeded,
	InvalidTweet,
	InvalidResponse,
	Network,
	Unauthorized,
	Unknown(String),
}

impl TwitterClient {

	pub fn new(consumer_key: String, consumer_secret: String, access_key: String, access_secret: String) -> TwitterClient
	{
		TwitterClient {
			consumer_key: consumer_key,
			consumer_secret: consumer_secret,
			access_key: access_key,
			access_secret: access_secret,
			client: Client::new(),
		}
	}

	pub fn is_valid(&self) -> bool
	{
		// TODO: implement token validation
		true
	}

	fn post(&self, url: &str, args: Vec<(&str, &str)>) -> Result<Response, ()>
	{
		let mut req = self.client.post(url);

		req.form(&args);

		let oauth_header = OAuthAuthorizationHeaderBuilder::new(
			"POST",
			&Url::parse(url).expect("must be a valid url string"),
			&self.consumer_key,
			&self.consumer_secret,
			HmacSha1)
			.token(&self.access_key, &self.access_secret)
			.request_parameters(args.into_iter())
			.finish_for_twitter()
			.to_string();

		req.header(Authorization(oauth_header)).send().map_err(|_| ())
	}

	pub fn update_status(&self, message: &str, in_reply_to: Option<u64>)
	 -> Result<u64, TwitterError>
	{
		let prev = in_reply_to.map(|i| i.to_string());
		let mut args = vec![("status", message)];

		if let Some(prev) = prev.as_ref() {
			args.push(("in_reply_to_status_id", prev));
		}

		let mut response = self.post(API_URL, args).map_err(|_| TwitterError::Network)?;

		match response.status() {

			StatusCode::Forbidden => {

				let mut body = String::new();
				response.read_to_string(&mut body).map_err(|_| TwitterError::Network)?;

				let json: Option<Value> = serde_json::from_str(&body).ok();
				let id = json.and_then(|json| json["errors"][0]["code"].as_i64());

				match id {
					Some(186) => Err(TwitterError::InvalidTweet),
					Some(187) => Err(TwitterError::Duplicated),
					_ => Err(TwitterError::Unknown(body))
				}
			},

			StatusCode::Ok => {

				let mut body = String::new();
				response.read_to_string(&mut body).map_err(|_| TwitterError::Network)?;

				let json: Value =
					serde_json::from_str(&body).map_err(|_| TwitterError::InvalidResponse)?;
				let id = json["id"].as_u64().ok_or(TwitterError::InvalidResponse)?;

				Ok(id)
			},

			StatusCode::TooManyRequests => Err(TwitterError::RateLimitExceeded),
			StatusCode::Unauthorized => Err(TwitterError::Unauthorized),
			_ => Err(TwitterError::Unknown(format!("unknown status: {}", response.status())))
		}
	}
}
<|endoftext|>


<commit_before>use bytes::{BufMut, BytesMut};
use std::io::{Error, ErrorKind};
use tokio::codec::{Decoder, Encoder};

// 5 MiB limit
const MAX_MSG_LEN: u32 = 5_242_880;
// 512 bits should be way more than enough
const MAX_ID_LEN: u8 = 64;

#[derive(Debug)]
pub struct Payload {
    id: BytesMut,
    msg: BytesMut,
}

#[derive(Default, Debug)]
pub struct Codec {
    id_len: u8,
    msg_len: u32,
}

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

impl Encoder for Codec {
    type Item = Payload;
    type Error = Error;

    fn encode(&mut self, item: Self::Item, buf: &mut BytesMut) -> Result<(), Error> {
        assert!(item.id.len() < usize::from(MAX_ID_LEN));
        assert!((item.msg.len() as u32) < MAX_MSG_LEN);
        buf.reserve(item.id.len() + item.msg.len() + 5);
        buf.put_u8(item.id.len() as u8);
        buf.put_u32_be(item.msg.len() as u32);
        buf.put(item.id);
        buf.put(item.msg);
        Ok(())
    }
}

impl Decoder for Codec {
    type Item = Payload;
    type Error = Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Error> {
        if self.id_len == 0 && self.msg_len == 0 && buf.len() >= 5 {
            let buf = buf.split_to(5);
            self.id_len = buf[0];
            self.msg_len = u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
            if self.id_len > MAX_ID_LEN {
                return Err(Error::new(
                    ErrorKind::Other,
                    format!("id must be <={} bytes", MAX_ID_LEN),
                ));
            } else if self.msg_len > MAX_MSG_LEN {
                return Err(Error::new(
                    ErrorKind::Other,
                    format!("msg must be <={} bytes", MAX_MSG_LEN),
                ));
            }
        }
        if self.id_len != 0
            && self.msg_len != 0
            && buf.len() >= usize::from(self.id_len) + (self.msg_len as usize)
        {
            let id_len = self.id_len;
            self.id_len = 0;

            let msg_len = self.msg_len;
            self.msg_len = 0;

            let id = buf.split_to(id_len as usize);
            let msg = buf.split_to(msg_len as usize);
            Ok(Some(Payload { id, msg }))
        } else {
            Ok(None)
        }
    }
}

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

    macro_rules! assert_decode {
        ($codec:expr, $bytes:expr, $id:expr, $msg:expr) => {{
            let res = $codec.decode(&mut $bytes).unwrap();
            assert!(res.is_some(), "decoding failure, expected Ok");
            let res = res.expect("decoding failure, expected payload");
            assert_eq!(res.id, $id, "id mismatch");
            assert_eq!(res.msg, $msg, "msg mismatch");
        }};
    }

    #[test]
    fn test_max_id_len() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![MAX_ID_LEN + 1, 0, 0, 0, 0]);
        let err = codec.decode(&mut bytes).unwrap_err();
        assert_eq!(
            err.description(),
            format!("id must be <={} bytes", MAX_ID_LEN)
        );
    }

    #[test]
    fn test_max_msg_len() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![1, 255, 255, 255, 255]);
        let err = codec.decode(&mut bytes).unwrap_err();
        assert_eq!(
            err.description(),
            format!("msg must be <={} bytes", MAX_MSG_LEN)
        );
    }

    #[test]
    fn test_encode_full_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::new();
        codec
            .encode(
                Payload {
                    id: vec![0, 1, 2, 3, 4, 5].into(),
                    msg: vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245].into(),
                },
                &mut bytes,
            )
            .unwrap();
        assert_eq!(
            bytes,
            vec![
                6, // ID len
                0, 0, 0, 11, // Msg len
                0, 1, 2, 3, 4, 5, // ID
                255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245 // Msg
            ]
        );
    }

    #[test]
    fn test_decode_full_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
        ]);

        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );
    }

    #[test]
    fn test_decode_multipart_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, // Msg
        ]);
        let res = codec.decode(&mut bytes).unwrap();
        assert!(res.is_none());

        bytes.reserve(3);
        bytes.put_slice(&vec![247, 246, 245]);
        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );
    }

    #[test]
    fn test_decode_multiple_multipart_frames() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
            3,   // ID len
        ]);
        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );

        // Ensure the codec data is correct
        assert_eq!(bytes.len(), 1);
        assert_eq!(codec.id_len, 0);
        assert_eq!(codec.msg_len, 0);

        bytes.reserve(4);
        bytes.put_slice(&vec![0, 0, 0, 5]); // Write msg len
        let res = codec.decode(&mut bytes).unwrap();
        assert!(res.is_none());

        assert_eq!(bytes.len(), 0);
        assert_eq!(codec.id_len, 3);
        assert_eq!(codec.msg_len, 5);

        bytes.reserve(8);
        bytes.put_slice(&vec![
            1, 1, 1, // ID
            2, 2, 2, 2, 2, // Msg
        ]);
        assert_decode!(codec, bytes, vec![1, 1, 1], vec![2, 2, 2, 2, 2]);
    }

    #[test]
    fn test_decode_multiple_full_frames() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            // First payload
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
            // Second payload
            3, 0, 0, 0, 4, 1, 1, 1, 2, 2, 2, 2, // Third payload
            4, 0, 0, 0, 5, 3, 3, 3, 3, 4, 4, 4, 4, 4,
        ]);

        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );

        assert_decode!(codec, bytes, vec![1, 1, 1], vec![2, 2, 2, 2]);

        assert_decode!(codec, bytes, vec![3, 3, 3, 3], vec![4, 4, 4, 4, 4]);

        assert_eq!(bytes.len(), 0);
        assert_eq!(codec.id_len, 0);
        assert_eq!(codec.msg_len, 0);
    }
}
<commit_msg>Preserve original formatting in some cases<commit_after>use bytes::{BufMut, BytesMut};
use std::io::{Error, ErrorKind};
use tokio::codec::{Decoder, Encoder};

// 5 MiB limit
const MAX_MSG_LEN: u32 = 5_242_880;
// 512 bits should be way more than enough
const MAX_ID_LEN: u8 = 64;

#[derive(Debug)]
pub struct Payload {
    id: BytesMut,
    msg: BytesMut,
}

#[derive(Default, Debug)]
pub struct Codec {
    id_len: u8,
    msg_len: u32,
}

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

impl Encoder for Codec {
    type Item = Payload;
    type Error = Error;

    fn encode(&mut self, item: Self::Item, buf: &mut BytesMut) -> Result<(), Error> {
        assert!(item.id.len() < usize::from(MAX_ID_LEN));
        assert!((item.msg.len() as u32) < MAX_MSG_LEN);
        buf.reserve(item.id.len() + item.msg.len() + 5);
        buf.put_u8(item.id.len() as u8);
        buf.put_u32_be(item.msg.len() as u32);
        buf.put(item.id);
        buf.put(item.msg);
        Ok(())
    }
}

impl Decoder for Codec {
    type Item = Payload;
    type Error = Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Error> {
        if self.id_len == 0 && self.msg_len == 0 && buf.len() >= 5 {
            let buf = buf.split_to(5);
            self.id_len = buf[0];
            self.msg_len = u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
            if self.id_len > MAX_ID_LEN {
                return Err(Error::new(
                    ErrorKind::Other,
                    format!("id must be <={} bytes", MAX_ID_LEN),
                ));
            } else if self.msg_len > MAX_MSG_LEN {
                return Err(Error::new(
                    ErrorKind::Other,
                    format!("msg must be <={} bytes", MAX_MSG_LEN),
                ));
            }
        }
        if self.id_len != 0
            && self.msg_len != 0
            && buf.len() >= usize::from(self.id_len) + (self.msg_len as usize)
        {
            let id_len = self.id_len;
            self.id_len = 0;

            let msg_len = self.msg_len;
            self.msg_len = 0;

            let id = buf.split_to(id_len as usize);
            let msg = buf.split_to(msg_len as usize);
            Ok(Some(Payload { id, msg }))
        } else {
            Ok(None)
        }
    }
}

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

    macro_rules! assert_decode {
        ($codec:expr, $bytes:expr, $id:expr, $msg:expr) => {{
            let res = $codec.decode(&mut $bytes).unwrap();
            assert!(res.is_some(), "decoding failure, expected Ok");
            let res = res.expect("decoding failure, expected payload");
            assert_eq!(res.id, $id, "id mismatch");
            assert_eq!(res.msg, $msg, "msg mismatch");
        }};
    }

    #[test]
    fn test_max_id_len() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![MAX_ID_LEN + 1, 0, 0, 0, 0]);
        let err = codec.decode(&mut bytes).unwrap_err();
        assert_eq!(
            err.description(),
            format!("id must be <={} bytes", MAX_ID_LEN)
        );
    }

    #[test]
    fn test_max_msg_len() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![1, 255, 255, 255, 255]);
        let err = codec.decode(&mut bytes).unwrap_err();
        assert_eq!(
            err.description(),
            format!("msg must be <={} bytes", MAX_MSG_LEN)
        );
    }

    #[test]
    fn test_encode_full_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::new();
        codec
            .encode(
                Payload {
                    id: vec![0, 1, 2, 3, 4, 5].into(),
                    msg: vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245].into(),
                },
                &mut bytes,
            )
            .unwrap();
        assert_eq!(
            bytes,
            vec![
                6, // ID len
                0, 0, 0, 11, // Msg len
                0, 1, 2, 3, 4, 5, // ID
                255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245 // Msg
            ]
        );
    }

    #[test]
    fn test_decode_full_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
        ]);

        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );
    }

    #[test]
    fn test_decode_multipart_frame() {
        let mut codec = Codec::new();
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, // Msg
        ]);
        let res = codec.decode(&mut bytes).unwrap();
        assert!(res.is_none());

        bytes.reserve(3);
        bytes.put_slice(&vec![247, 246, 245]);
        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );
    }

    #[test]
    fn test_decode_multiple_multipart_frames() {
        let mut codec = Codec::new();
        #[rustfmt::skip]
        let mut bytes = BytesMut::from(vec![
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
            3, // ID len
        ]);
        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );

        // Ensure the codec data is correct
        assert_eq!(bytes.len(), 1);
        assert_eq!(codec.id_len, 0);
        assert_eq!(codec.msg_len, 0);

        bytes.reserve(4);
        bytes.put_slice(&vec![0, 0, 0, 5]); // Write msg len
        let res = codec.decode(&mut bytes).unwrap();
        assert!(res.is_none());

        assert_eq!(bytes.len(), 0);
        assert_eq!(codec.id_len, 3);
        assert_eq!(codec.msg_len, 5);

        bytes.reserve(8);
        bytes.put_slice(&vec![
            1, 1, 1, // ID
            2, 2, 2, 2, 2, // Msg
        ]);
        assert_decode!(codec, bytes, vec![1, 1, 1], vec![2, 2, 2, 2, 2]);
    }

    #[test]
    fn test_decode_multiple_full_frames() {
        let mut codec = Codec::new();
        #[rustfmt::skip]
        let mut bytes = BytesMut::from(vec![
            // First payload
            6, // ID len
            0, 0, 0, 11, // Msg len
            0, 1, 2, 3, 4, 5, // ID
            255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, // Msg
            // Second payload
            3,
            0, 0, 0, 4,
            1, 1, 1,
            2, 2, 2, 2,
            // Third payload
            4,
            0, 0, 0, 5,
            3, 3, 3, 3,
            4, 4, 4, 4, 4
        ]);

        assert_decode!(
            codec,
            bytes,
            vec![0, 1, 2, 3, 4, 5],
            vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245]
        );

        assert_decode!(codec, bytes, vec![1, 1, 1], vec![2, 2, 2, 2]);

        assert_decode!(codec, bytes, vec![3, 3, 3, 3], vec![4, 4, 4, 4, 4]);

        assert_eq!(bytes.len(), 0);
        assert_eq!(codec.id_len, 0);
        assert_eq!(codec.msg_len, 0);
    }
}
<|endoftext|>