<commit_before>use av_format::error::*;
use std::io::SeekFrom;
use av_data::packet::Packet;
use av_data::timeinfo::TimeInfo;
use av_format::stream::*;
use av_format::buffer::Buffered;
use av_format::demuxer::demux::{Demuxer, Event};
use av_format::demuxer::context::GlobalInfo;
use std::collections::VecDeque;
use rational::Rational64;

use ebml::{ebml_header, EBMLHeader};
use elements::{segment, segment_element, Cluster, SeekHead, Info, Tracks, TrackEntry,
               SegmentElement, simple_block};
use nom::{self, Err, IResult, Offset};

#[derive(Debug, Clone, PartialEq)]
pub struct MkvDemuxer {
    pub header: Option<EBMLHeader>,
    pub seek_head: Option<SeekHead>,
    pub info: Option<Info>,
    pub tracks: Option<Tracks>,
    pub queue: VecDeque<Event>,
    pub blockstream: Vec<u8>,
}

impl MkvDemuxer {
    pub fn new() -> MkvDemuxer {
        MkvDemuxer {
            header: None,
            seek_head: None,
            info: None,
            tracks: None,
            queue: VecDeque::new(),
            blockstream: Vec::new(),
        }
    }

    pub fn parse_until_tracks<'a>(&mut self, original_input: &'a [u8]) -> IResult<&'a [u8], ()> {
        let (i1, header) = try_parse!(original_input, ebml_header);

        self.header = Some(header);

        let (mut input, _) = try_parse!(i1, segment);

        loop {
            if self.seek_head.is_some() && self.info.is_some() && self.tracks.is_some() {
                return Ok((input, ()));
            }

            println!("offset: {}", original_input.offset(input));

            let (i3, element) = try_parse!(input, segment_element);

            match element {
                SegmentElement::SeekHead(s) => {
                    println!("got seek head: {:#?}", s);
                    if self.seek_head.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.seek_head = Some(s);
                    }
                }
                SegmentElement::Info(i) => {
                    println!("got info: {:#?}", i);
                    if self.info.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.info = Some(i);
                    }
                }
                SegmentElement::Tracks(t) => {
                    println!("got tracks: {:#?}", t);
                    if self.tracks.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.tracks = Some(t);
                    }
                }
                SegmentElement::Cluster(c) => {
                    println!("got a cluster: {:#?}", c);
                    //self.clusters.push(c);
                }
                el => {
                    println!("got element: {:#?}", el);
                }
            }

            input = i3;
        }
    }
}

impl Demuxer for MkvDemuxer {
    fn open(&mut self) {}

    fn read_headers(&mut self, buf: &Box<Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom> {
        match self.parse_until_tracks(buf.data()) {
            Ok((i, _)) => {
                info.duration = self.info.as_ref().and_then(|info| info.duration).map(|d| d as u64);
                if let Some(ref t) = self.tracks {
                    info.streams = t.tracks.iter().map(|tr| track_to_stream(tr)).collect();
                }
                Ok(SeekFrom::Current(buf.data().offset(i) as i64))
            }
             Err(Err::Incomplete(_)) => Err(ErrorKind::MoreDataNeeded.into()),
            e => {
                println!("error reading headers: {:?}", e);
                Err(ErrorKind::InvalidData.into())
            }
        }
    }

    fn read_packet(&mut self, buf: &Box<Buffered>) -> Result<(SeekFrom, Event)> {
        if let Some(event) = self.queue.pop_front() {
            Ok((SeekFrom::Current(0), event))
        } else {
            println!("no more stored packet, parsing a new one");
            match segment_element(buf.data()) {
                Ok((i, element)) => {
                    let seek = SeekFrom::Current(buf.data().offset(i) as i64);
                    match element {
                        SegmentElement::Cluster(c) => {
                            //self.clusters.push(c);
                            println!("got cluster element at timecode: {}", c.timecode);
                            let mut packets = c.generate_packets();
                            self.queue.extend(packets.drain(..));
                            /*
                        for block in c.simple_block.iter() {
                          println!("got simple block of size {}", block.len());
                          if let IResult::Done(_,o) = simple_block(block) {
                            println!("parsing simple block: {:?}", o);
                          } else {
                            println!("error parsing simple block");
                          }
                          self.blockstream.extend(*block);
                        }

                        for block_group in c.block_group.iter() {
                          println!("got block group of size {}", block_group.block.len());
                          self.blockstream.extend(block_group.block);
                        }
                        */
                            if let Some(event) = self.queue.pop_front() {
                                return Ok((seek, event));
                            }
                        }
                        el => {
                            println!("got element: {:#?}", el);
                        }
                    }

                    Ok((seek, Event::MoreDataNeeded))
                }
                 Err(Err::Incomplete(_)) => Ok((SeekFrom::Current(0), Event::MoreDataNeeded)),
                e => {
                    println!("parsing issue: {:?}", e);
                    Err(ErrorKind::InvalidData.into())
                }
            }
        }
    }
}

fn track_entry_codec_id(t: &TrackEntry) -> Option<CodecID> {
    // TODO: Support V_QUICKTIME and V_MS/VFW/FOURCC
    match t.codec_id.as_ref() {
        "A_OPUS" => Some(CodecID::Opus),
        "V_VP9" => Some(CodecID::VP9),
        _ => None,
    }
}

fn track_entry_video_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref video) = t.video {
        let v = VideoInfo {
            width: video.pixel_width as usize,
            height: video.pixel_height as usize,
            // TODO parse Colour and/or CodecPrivate to extract the format
            format: None,
        };
        Some(MediaKind::Video(v))
    } else {
        None
    }
}

fn track_entry_audio_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref audio) = t.audio {
        let rate = if let Some(r) = audio.output_sampling_frequency {
            r
        } else {
            audio.sampling_frequency
        };
        // TODO: complete it
        let a = AudioInfo {
            samples: 0,
            rate: rate as usize,
            map: None,
            format: None,
        };
        Some(MediaKind::Audio(a))
    } else {
        None
    }
}

fn track_entry_media_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Use an enum for the track type
    match t.track_type {
        0x1 => track_entry_video_kind(t),
        0x2 => track_entry_audio_kind(t),
        _ => None,
    }
}

pub fn track_to_stream(t: &TrackEntry) -> Stream {
    Stream {
        id: t.track_uid as usize,
        index: t.track_number as usize,
        start: None,
        duration: t.default_duration,
        timebase: Rational64::from_integer(1),
        // TODO: Extend CodecParams and fill it with the remaining information
        params: CodecParams {
            extradata: t.codec_private.clone(),
            bit_rate: 0,
            codec_id: track_entry_codec_id(t),
            kind: track_entry_media_kind(t),
        },
    }
}

impl<'a> Cluster<'a> {
    pub fn generate_packets(&self) -> Vec<Event> {
        let mut v = Vec::new();

        for block_data in self.simple_block.iter() {
            if let Ok((i, block)) = simple_block(block_data) {
                //println!("parsing simple block: {:?}", block);
                let packet = Packet {
                    data: i.into(),
                    t: TimeInfo {
                        pts: Some(block.timecode as i64),
                        dts: None,
                        duration: None,
                        timebase: None,
                    },
                    pos: None,
                    stream_index: block.track_number as isize,
                    is_key: block.keyframe,
                    is_corrupted: false,
                };

                v.push(Event::NewPacket(packet));
            } else {
                println!("error parsing simple block");
            }
        }

        v
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use nom::Offset;
    use av_format::demuxer::context::DemuxerContext;

    const webm: &'static [u8] = include_bytes!("../assets/bbb-vp9-opus.webm");

    #[test]
    fn parse_headers() {
        let mut demuxer = MkvDemuxer::new();

        let res = demuxer.parse_until_tracks(webm);
        println!("got parsing res: {:?}", res);
        match res {
            Ok((i, _)) => {
                println!("offset: {} bytes", webm.offset(i));
            }
            e => {
                println!("could not parse: {:?}", e);
            }
        }

        println!("demuxer: {:#?}", demuxer);
        panic!();
    }

    #[test]
    fn context() {
        let mut context = DemuxerContext::new(Box::new(MkvDemuxer::new()),
                                              Box::new(Cursor::new(webm)));
        println!("DEMUXER CONTEXT read headers: {:?}", context.read_headers());
        println!("DEMUXER CONTEXT streams: {:?}", context.info.streams);
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        println!("DEMUXER CONTEXT event: {:?}", context.read_packet());
        panic!();
    }
}
<commit_msg>Update to the current format API<commit_after>use av_format::error::*;
use std::io::SeekFrom;
use av_data::packet::Packet;
use av_data::timeinfo::TimeInfo;
use av_format::stream::*;
use av_format::buffer::Buffered;
use av_format::demuxer::{Demuxer, Event};
use av_format::demuxer::GlobalInfo;
use std::collections::VecDeque;
use rational::Rational64;

use ebml::{ebml_header, EBMLHeader};
use elements::{segment, segment_element, Cluster, SeekHead, Info, Tracks, TrackEntry,
               SegmentElement, simple_block};
use nom::{self, Err, IResult, Offset};

#[derive(Debug, Clone, PartialEq)]
pub struct MkvDemuxer {
    pub header: Option<EBMLHeader>,
    pub seek_head: Option<SeekHead>,
    pub info: Option<Info>,
    pub tracks: Option<Tracks>,
    pub queue: VecDeque<Event>,
    pub blockstream: Vec<u8>,
}

impl MkvDemuxer {
    pub fn new() -> MkvDemuxer {
        MkvDemuxer {
            header: None,
            seek_head: None,
            info: None,
            tracks: None,
            queue: VecDeque::new(),
            blockstream: Vec::new(),
        }
    }

    pub fn parse_until_tracks<'a>(&mut self, original_input: &'a [u8]) -> IResult<&'a [u8], ()> {
        let (i1, header) = try_parse!(original_input, ebml_header);

        self.header = Some(header);

        let (mut input, _) = try_parse!(i1, segment);

        loop {
            if self.seek_head.is_some() && self.info.is_some() && self.tracks.is_some() {
                return Ok((input, ()));
            }

            println!("offset: {}", original_input.offset(input));

            let (i3, element) = try_parse!(input, segment_element);

            match element {
                SegmentElement::SeekHead(s) => {
                    println!("got seek head: {:#?}", s);
                    if self.seek_head.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.seek_head = Some(s);
                    }
                }
                SegmentElement::Info(i) => {
                    println!("got info: {:#?}", i);
                    if self.info.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.info = Some(i);
                    }
                }
                SegmentElement::Tracks(t) => {
                    println!("got tracks: {:#?}", t);
                    if self.tracks.is_some() {
                        return Err(Err::Error(error_position!(nom::ErrorKind::Custom(1), input)));
                    } else {
                        self.tracks = Some(t);
                    }
                }
                SegmentElement::Cluster(c) => {
                    println!("got a cluster: {:#?}", c);
                    //self.clusters.push(c);
                }
                el => {
                    println!("got element: {:#?}", el);
                }
            }

            input = i3;
        }
    }
}

impl Demuxer for MkvDemuxer {
    fn read_headers(&mut self, buf: &Box<Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom> {
        match self.parse_until_tracks(buf.data()) {
            Ok((i, _)) => {
                info.duration = self.info.as_ref().and_then(|info| info.duration).map(|d| d as u64);
                if let Some(ref t) = self.tracks {
                    info.streams = t.tracks.iter().map(|tr| track_to_stream(tr)).collect();
                }
                Ok(SeekFrom::Current(buf.data().offset(i) as i64))
            }
             Err(Err::Incomplete(_)) => Err(ErrorKind::MoreDataNeeded.into()),
            e => {
                println!("error reading headers: {:?}", e);
                Err(ErrorKind::InvalidData.into())
            }
        }
    }

    fn read_event(&mut self, buf: &Box<Buffered>) -> Result<(SeekFrom, Event)> {
        if let Some(event) = self.queue.pop_front() {
            Ok((SeekFrom::Current(0), event))
        } else {
            println!("no more stored packet, parsing a new one");
            match segment_element(buf.data()) {
                Ok((i, element)) => {
                    let seek = SeekFrom::Current(buf.data().offset(i) as i64);
                    match element {
                        SegmentElement::Cluster(c) => {
                            //self.clusters.push(c);
                            println!("got cluster element at timecode: {}", c.timecode);
                            let mut packets = c.generate_packets();
                            self.queue.extend(packets.drain(..));
                            /*
                        for block in c.simple_block.iter() {
                          println!("got simple block of size {}", block.len());
                          if let IResult::Done(_,o) = simple_block(block) {
                            println!("parsing simple block: {:?}", o);
                          } else {
                            println!("error parsing simple block");
                          }
                          self.blockstream.extend(*block);
                        }

                        for block_group in c.block_group.iter() {
                          println!("got block group of size {}", block_group.block.len());
                          self.blockstream.extend(block_group.block);
                        }
                        */
                            if let Some(event) = self.queue.pop_front() {
                                return Ok((seek, event));
                            }
                        }
                        el => {
                            println!("got element: {:#?}", el);
                        }
                    }

                    Ok((seek, Event::MoreDataNeeded))
                }
                 Err(Err::Incomplete(_)) => Ok((SeekFrom::Current(0), Event::MoreDataNeeded)),
                e => {
                    println!("parsing issue: {:?}", e);
                    Err(ErrorKind::InvalidData.into())
                }
            }
        }
    }
}

fn track_entry_codec_id(t: &TrackEntry) -> Option<CodecID> {
    // TODO: Support V_QUICKTIME and V_MS/VFW/FOURCC
    match t.codec_id.as_ref() {
        "A_OPUS" => Some(CodecID::Opus),
        "V_VP9" => Some(CodecID::VP9),
        _ => None,
    }
}

fn track_entry_video_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref video) = t.video {
        let v = VideoInfo {
            width: video.pixel_width as usize,
            height: video.pixel_height as usize,
            // TODO parse Colour and/or CodecPrivate to extract the format
            format: None,
        };
        Some(MediaKind::Video(v))
    } else {
        None
    }
}

fn track_entry_audio_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Validate that a track::video exists for track::type video before.
    if let Some(ref audio) = t.audio {
        let rate = if let Some(r) = audio.output_sampling_frequency {
            r
        } else {
            audio.sampling_frequency
        };
        // TODO: complete it
        let a = AudioInfo {
            samples: 0,
            rate: rate as usize,
            map: None,
            format: None,
        };
        Some(MediaKind::Audio(a))
    } else {
        None
    }
}

fn track_entry_media_kind(t: &TrackEntry) -> Option<MediaKind> {
    // TODO: Use an enum for the track type
    match t.track_type {
        0x1 => track_entry_video_kind(t),
        0x2 => track_entry_audio_kind(t),
        _ => None,
    }
}

pub fn track_to_stream(t: &TrackEntry) -> Stream {
    Stream {
        id: t.track_uid as usize,
        index: t.track_number as usize,
        start: None,
        duration: t.default_duration,
        timebase: Rational64::from_integer(1),
        // TODO: Extend CodecParams and fill it with the remaining information
        params: CodecParams {
            extradata: t.codec_private.clone(),
            bit_rate: 0,
            codec_id: track_entry_codec_id(t),
            kind: track_entry_media_kind(t),
        },
    }
}

impl<'a> Cluster<'a> {
    pub fn generate_packets(&self) -> Vec<Event> {
        let mut v = Vec::new();

        for block_data in self.simple_block.iter() {
            if let Ok((i, block)) = simple_block(block_data) {
                //println!("parsing simple block: {:?}", block);
                let packet = Packet {
                    data: i.into(),
                    t: TimeInfo {
                        pts: Some(block.timecode as i64),
                        dts: None,
                        duration: None,
                        timebase: None,
                    },
                    pos: None,
                    stream_index: block.track_number as isize,
                    is_key: block.keyframe,
                    is_corrupted: false,
                };

                v.push(Event::NewPacket(packet));
            } else {
                println!("error parsing simple block");
            }
        }

        v
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use nom::Offset;
    use av_format::demuxer::Context;

    const webm: &'static [u8] = include_bytes!("../assets/bbb-vp9-opus.webm");

    #[test]
    fn parse_headers() {
        let mut demuxer = MkvDemuxer::new();

        let res = demuxer.parse_until_tracks(webm);
        println!("got parsing res: {:?}", res);
        match res {
            Ok((i, _)) => {
                println!("offset: {} bytes", webm.offset(i));
            }
            e => {
                println!("could not parse: {:?}", e);
            }
        }

        println!("demuxer: {:#?}", demuxer);
        panic!();
    }

    #[test]
    fn context() {
        let mut context = Context::new(Box::new(MkvDemuxer::new()),
                                       Box::new(Cursor::new(webm)));
        println!("DEMUXER CONTEXT read headers: {:?}", context.read_headers());
        println!("DEMUXER CONTEXT streams: {:?}", context.info.streams);
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        println!("DEMUXER CONTEXT event: {:?}", context.read_event());
        panic!();
    }
}
<|endoftext|>


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

//! Safe and rich Rust wrapper around the Vulkan API.
//!
//! # Brief summary of Vulkan
//!
//! - The [`Instance`](instance/struct.Instance.html) object is the API entry point. It is the
//!   first object you must create before starting to use Vulkan.
//!
//! - The [`PhysicalDevice`](instance/struct.PhysicalDevice.html) object represents an
//!   implementation of Vulkan available on the system (eg. a graphics card, a software
//!   implementation, etc.). Physical devices can be enumerated from an instance with
//!   [`PhysicalDevice::enumerate()`](instance/struct.PhysicalDevice.html#method.enumerate).
//!
//! - Once you have chosen a physical device to use, you can create a
//!   [`Device`](device/index.html) object from it. The `Device` is the most important
//!   object of Vulkan, as it represents an open channel of communicaton with a physical device.
//!   You always need to have one before you can do something interesting things with Vulkan.
//!
//! - [*Buffers*](buffer/index.html) and [*images*](image/index.html) can be used to store data on
//!   memory accessible by the GPU (or more generally by the Vulkan implementation). Buffers are
//!   usually used to store information about vertices, lights, etc. or arbitrary data, while
//!   images are used to store textures or multi-dimensional data.
//!
//! - In order to show something on the screen, you need a [`Swapchain`](swapchain/index.html).
//!   A `Swapchain` contains special `Image`s that correspond to the content of the window or the
//!   monitor. When you *present* a swapchain, the content of one of these special images is shown
//!   on the screen.
//!
//! - In order to ask the GPU to do something, you must create a
//!   [*command buffer*](command_buffer/index.html). A command buffer contains a list of commands
//!   that the GPU must perform. This can include copies between buffers and images, compute
//!   operations, or graphics operations. For the work to start, the command buffer must then be
//!   submitted to a [`Queue`](device/struct.Queue.html), which is obtained when you create the
//!   `Device`.
//!
//! - In order to be able to add a compute operation or a graphics operation to a command buffer,
//!   you need to have created a [`ComputePipeline` or a `GraphicsPipeline`
//!   object](pipeline/index.html) that describes the operation you want. These objects are usually
//!   created during your program's initialization. `Shader`s are programs that the GPU will
//!   execute as part of a pipeline. [*Descriptors*](descriptor/index.html) can be used to access
//!   the content of buffers or images from within shaders.
//!
//! - For graphical operations, [`RenderPass`es and `Framebuffer`s](framebuffer/index.html)
//!   describe on which images the implementation must draw upon.
//!
//! - Once you have built a *command buffer* that contains a list of commands, submitting it to the
//!   GPU will return an object that implements [the `GpuFuture` trait](sync/index.html).
//!   `GpuFuture`s allow you to chain multiple submissions together and are essential to performing
//!   multiple operations on multiple different GPU queues.
//!

//#![warn(missing_docs)]        // TODO: activate
#![allow(dead_code)]            // TODO: remove
#![allow(unused_variables)]     // TODO: remove

extern crate crossbeam;
extern crate fnv;
#[macro_use]
extern crate lazy_static;
extern crate shared_library;
extern crate smallvec;
extern crate vk_sys as vk;

#[macro_use]
mod tests;

mod features;
mod version;

pub mod buffer;
pub mod command_buffer;
pub mod descriptor;
pub mod device;
pub mod format;
#[macro_use]
pub mod framebuffer;
pub mod image;
pub mod instance;
pub mod memory;
pub mod pipeline;
pub mod query;
pub mod sampler;
pub mod swapchain;
pub mod sync;

use std::error;
use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::MutexGuard;

/// Alternative to the `Deref` trait. Contrary to `Deref`, must always return the same object.
pub unsafe trait SafeDeref: Deref {}
unsafe impl<'a, T: ?Sized> SafeDeref for &'a T {}
unsafe impl<T: ?Sized> SafeDeref for Arc<T> {}
unsafe impl<T: ?Sized> SafeDeref for Box<T> {}

/// Gives access to the internal identifier of an object.
pub unsafe trait VulkanObject {
    /// The type of the object.
    type Object;

    /// Returns a reference to the object.
    fn internal_object(&self) -> Self::Object;
}

/// Gives access to the internal identifier of an object.
// TODO: remove ; crappy design
pub unsafe trait SynchronizedVulkanObject {
    /// The type of the object.
    type Object;

    /// Returns a reference to the object.
    fn internal_object_guard(&self) -> MutexGuard<Self::Object>;
}

/// Gives access to the Vulkan function pointers stored in this object.
trait VulkanPointers {
    /// The struct that provides access to the function pointers.
    type Pointers;

    // Returns a reference to the pointers.
    fn pointers(&self) -> &Self::Pointers;
}

/// Error type returned by most Vulkan functions.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OomError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
}

impl error::Error for OomError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            OomError::OutOfHostMemory => "no memory available on the host",
            OomError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for OomError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for OomError {
    #[inline]
    fn from(err: Error) -> OomError {
        match err {
            Error::OutOfHostMemory => OomError::OutOfHostMemory,
            Error::OutOfDeviceMemory => OomError::OutOfDeviceMemory,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// All possible success codes returned by any Vulkan function.
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
enum Success {
    Success = vk::SUCCESS,
    NotReady = vk::NOT_READY,
    Timeout = vk::TIMEOUT,
    EventSet = vk::EVENT_SET,
    EventReset = vk::EVENT_RESET,
    Incomplete = vk::INCOMPLETE,
    Suboptimal = vk::SUBOPTIMAL_KHR,
}

/// All possible errors returned by any Vulkan function.
///
/// This type is not public. Instead all public error types should implement `From<Error>` and
/// panic for error code that arent supposed to happen.
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
#[doc(hidden)]      // TODO: this is necessary because of the stupid visibility rules in rustc
pub enum Error {
    OutOfHostMemory = vk::ERROR_OUT_OF_HOST_MEMORY,
    OutOfDeviceMemory = vk::ERROR_OUT_OF_DEVICE_MEMORY,
    InitializationFailed = vk::ERROR_INITIALIZATION_FAILED,
    DeviceLost = vk::ERROR_DEVICE_LOST,
    MemoryMapFailed = vk::ERROR_MEMORY_MAP_FAILED,
    LayerNotPresent = vk::ERROR_LAYER_NOT_PRESENT,
    ExtensionNotPresent = vk::ERROR_EXTENSION_NOT_PRESENT,
    FeatureNotPresent = vk::ERROR_FEATURE_NOT_PRESENT,
    IncompatibleDriver = vk::ERROR_INCOMPATIBLE_DRIVER,
    TooManyObjects = vk::ERROR_TOO_MANY_OBJECTS,
    FormatNotSupported = vk::ERROR_FORMAT_NOT_SUPPORTED,
    SurfaceLost = vk::ERROR_SURFACE_LOST_KHR,
    NativeWindowInUse = vk::ERROR_NATIVE_WINDOW_IN_USE_KHR,
    OutOfDate = vk::ERROR_OUT_OF_DATE_KHR,
    IncompatibleDisplay = vk::ERROR_INCOMPATIBLE_DISPLAY_KHR,
    ValidationFailed = vk::ERROR_VALIDATION_FAILED_EXT,
    OutOfPoolMemory = vk::ERROR_OUT_OF_POOL_MEMORY_KHR,
}

/// Checks whether the result returned correctly.
fn check_errors(result: vk::Result) -> Result<Success, Error> {
    match result {
        vk::SUCCESS => Ok(Success::Success),
        vk::NOT_READY => Ok(Success::NotReady),
        vk::TIMEOUT => Ok(Success::Timeout),
        vk::EVENT_SET => Ok(Success::EventSet),
        vk::EVENT_RESET => Ok(Success::EventReset),
        vk::INCOMPLETE => Ok(Success::Incomplete),
        vk::ERROR_OUT_OF_HOST_MEMORY => Err(Error::OutOfHostMemory),
        vk::ERROR_OUT_OF_DEVICE_MEMORY => Err(Error::OutOfDeviceMemory),
        vk::ERROR_INITIALIZATION_FAILED => Err(Error::InitializationFailed),
        vk::ERROR_DEVICE_LOST => Err(Error::DeviceLost),
        vk::ERROR_MEMORY_MAP_FAILED => Err(Error::MemoryMapFailed),
        vk::ERROR_LAYER_NOT_PRESENT => Err(Error::LayerNotPresent),
        vk::ERROR_EXTENSION_NOT_PRESENT => Err(Error::ExtensionNotPresent),
        vk::ERROR_FEATURE_NOT_PRESENT => Err(Error::FeatureNotPresent),
        vk::ERROR_INCOMPATIBLE_DRIVER => Err(Error::IncompatibleDriver),
        vk::ERROR_TOO_MANY_OBJECTS => Err(Error::TooManyObjects),
        vk::ERROR_FORMAT_NOT_SUPPORTED => Err(Error::FormatNotSupported),
        vk::ERROR_SURFACE_LOST_KHR => Err(Error::SurfaceLost),
        vk::ERROR_NATIVE_WINDOW_IN_USE_KHR => Err(Error::NativeWindowInUse),
        vk::SUBOPTIMAL_KHR => Ok(Success::Suboptimal),
        vk::ERROR_OUT_OF_DATE_KHR => Err(Error::OutOfDate),
        vk::ERROR_INCOMPATIBLE_DISPLAY_KHR => Err(Error::IncompatibleDisplay),
        vk::ERROR_VALIDATION_FAILED_EXT => Err(Error::ValidationFailed),
        vk::ERROR_OUT_OF_POOL_MEMORY_KHR => Err(Error::OutOfPoolMemory),
        c => unreachable!("Unexpected error code returned by Vulkan: {}", c)
    }
}
<commit_msg>fix typo<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Safe and rich Rust wrapper around the Vulkan API.
//!
//! # Brief summary of Vulkan
//!
//! - The [`Instance`](instance/struct.Instance.html) object is the API entry point. It is the
//!   first object you must create before starting to use Vulkan.
//!
//! - The [`PhysicalDevice`](instance/struct.PhysicalDevice.html) object represents an
//!   implementation of Vulkan available on the system (eg. a graphics card, a software
//!   implementation, etc.). Physical devices can be enumerated from an instance with
//!   [`PhysicalDevice::enumerate()`](instance/struct.PhysicalDevice.html#method.enumerate).
//!
//! - Once you have chosen a physical device to use, you can create a
//!   [`Device`](device/index.html) object from it. The `Device` is the most important
//!   object of Vulkan, as it represents an open channel of communicaton with a physical device.
//!   You always need to have one before you can do interesting things with Vulkan.
//!
//! - [*Buffers*](buffer/index.html) and [*images*](image/index.html) can be used to store data on
//!   memory accessible by the GPU (or more generally by the Vulkan implementation). Buffers are
//!   usually used to store information about vertices, lights, etc. or arbitrary data, while
//!   images are used to store textures or multi-dimensional data.
//!
//! - In order to show something on the screen, you need a [`Swapchain`](swapchain/index.html).
//!   A `Swapchain` contains special `Image`s that correspond to the content of the window or the
//!   monitor. When you *present* a swapchain, the content of one of these special images is shown
//!   on the screen.
//!
//! - In order to ask the GPU to do something, you must create a
//!   [*command buffer*](command_buffer/index.html). A command buffer contains a list of commands
//!   that the GPU must perform. This can include copies between buffers and images, compute
//!   operations, or graphics operations. For the work to start, the command buffer must then be
//!   submitted to a [`Queue`](device/struct.Queue.html), which is obtained when you create the
//!   `Device`.
//!
//! - In order to be able to add a compute operation or a graphics operation to a command buffer,
//!   you need to have created a [`ComputePipeline` or a `GraphicsPipeline`
//!   object](pipeline/index.html) that describes the operation you want. These objects are usually
//!   created during your program's initialization. `Shader`s are programs that the GPU will
//!   execute as part of a pipeline. [*Descriptors*](descriptor/index.html) can be used to access
//!   the content of buffers or images from within shaders.
//!
//! - For graphical operations, [`RenderPass`es and `Framebuffer`s](framebuffer/index.html)
//!   describe on which images the implementation must draw upon.
//!
//! - Once you have built a *command buffer* that contains a list of commands, submitting it to the
//!   GPU will return an object that implements [the `GpuFuture` trait](sync/index.html).
//!   `GpuFuture`s allow you to chain multiple submissions together and are essential to performing
//!   multiple operations on multiple different GPU queues.
//!

//#![warn(missing_docs)]        // TODO: activate
#![allow(dead_code)]            // TODO: remove
#![allow(unused_variables)]     // TODO: remove

extern crate crossbeam;
extern crate fnv;
#[macro_use]
extern crate lazy_static;
extern crate shared_library;
extern crate smallvec;
extern crate vk_sys as vk;

#[macro_use]
mod tests;

mod features;
mod version;

pub mod buffer;
pub mod command_buffer;
pub mod descriptor;
pub mod device;
pub mod format;
#[macro_use]
pub mod framebuffer;
pub mod image;
pub mod instance;
pub mod memory;
pub mod pipeline;
pub mod query;
pub mod sampler;
pub mod swapchain;
pub mod sync;

use std::error;
use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::MutexGuard;

/// Alternative to the `Deref` trait. Contrary to `Deref`, must always return the same object.
pub unsafe trait SafeDeref: Deref {}
unsafe impl<'a, T: ?Sized> SafeDeref for &'a T {}
unsafe impl<T: ?Sized> SafeDeref for Arc<T> {}
unsafe impl<T: ?Sized> SafeDeref for Box<T> {}

/// Gives access to the internal identifier of an object.
pub unsafe trait VulkanObject {
    /// The type of the object.
    type Object;

    /// Returns a reference to the object.
    fn internal_object(&self) -> Self::Object;
}

/// Gives access to the internal identifier of an object.
// TODO: remove ; crappy design
pub unsafe trait SynchronizedVulkanObject {
    /// The type of the object.
    type Object;

    /// Returns a reference to the object.
    fn internal_object_guard(&self) -> MutexGuard<Self::Object>;
}

/// Gives access to the Vulkan function pointers stored in this object.
trait VulkanPointers {
    /// The struct that provides access to the function pointers.
    type Pointers;

    // Returns a reference to the pointers.
    fn pointers(&self) -> &Self::Pointers;
}

/// Error type returned by most Vulkan functions.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OomError {
    /// There is no memory available on the host (ie. the CPU, RAM, etc.).
    OutOfHostMemory,
    /// There is no memory available on the device (ie. video memory).
    OutOfDeviceMemory,
}

impl error::Error for OomError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            OomError::OutOfHostMemory => "no memory available on the host",
            OomError::OutOfDeviceMemory => "no memory available on the graphical device",
        }
    }
}

impl fmt::Display for OomError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for OomError {
    #[inline]
    fn from(err: Error) -> OomError {
        match err {
            Error::OutOfHostMemory => OomError::OutOfHostMemory,
            Error::OutOfDeviceMemory => OomError::OutOfDeviceMemory,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// All possible success codes returned by any Vulkan function.
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
enum Success {
    Success = vk::SUCCESS,
    NotReady = vk::NOT_READY,
    Timeout = vk::TIMEOUT,
    EventSet = vk::EVENT_SET,
    EventReset = vk::EVENT_RESET,
    Incomplete = vk::INCOMPLETE,
    Suboptimal = vk::SUBOPTIMAL_KHR,
}

/// All possible errors returned by any Vulkan function.
///
/// This type is not public. Instead all public error types should implement `From<Error>` and
/// panic for error code that arent supposed to happen.
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
#[doc(hidden)]      // TODO: this is necessary because of the stupid visibility rules in rustc
pub enum Error {
    OutOfHostMemory = vk::ERROR_OUT_OF_HOST_MEMORY,
    OutOfDeviceMemory = vk::ERROR_OUT_OF_DEVICE_MEMORY,
    InitializationFailed = vk::ERROR_INITIALIZATION_FAILED,
    DeviceLost = vk::ERROR_DEVICE_LOST,
    MemoryMapFailed = vk::ERROR_MEMORY_MAP_FAILED,
    LayerNotPresent = vk::ERROR_LAYER_NOT_PRESENT,
    ExtensionNotPresent = vk::ERROR_EXTENSION_NOT_PRESENT,
    FeatureNotPresent = vk::ERROR_FEATURE_NOT_PRESENT,
    IncompatibleDriver = vk::ERROR_INCOMPATIBLE_DRIVER,
    TooManyObjects = vk::ERROR_TOO_MANY_OBJECTS,
    FormatNotSupported = vk::ERROR_FORMAT_NOT_SUPPORTED,
    SurfaceLost = vk::ERROR_SURFACE_LOST_KHR,
    NativeWindowInUse = vk::ERROR_NATIVE_WINDOW_IN_USE_KHR,
    OutOfDate = vk::ERROR_OUT_OF_DATE_KHR,
    IncompatibleDisplay = vk::ERROR_INCOMPATIBLE_DISPLAY_KHR,
    ValidationFailed = vk::ERROR_VALIDATION_FAILED_EXT,
    OutOfPoolMemory = vk::ERROR_OUT_OF_POOL_MEMORY_KHR,
}

/// Checks whether the result returned correctly.
fn check_errors(result: vk::Result) -> Result<Success, Error> {
    match result {
        vk::SUCCESS => Ok(Success::Success),
        vk::NOT_READY => Ok(Success::NotReady),
        vk::TIMEOUT => Ok(Success::Timeout),
        vk::EVENT_SET => Ok(Success::EventSet),
        vk::EVENT_RESET => Ok(Success::EventReset),
        vk::INCOMPLETE => Ok(Success::Incomplete),
        vk::ERROR_OUT_OF_HOST_MEMORY => Err(Error::OutOfHostMemory),
        vk::ERROR_OUT_OF_DEVICE_MEMORY => Err(Error::OutOfDeviceMemory),
        vk::ERROR_INITIALIZATION_FAILED => Err(Error::InitializationFailed),
        vk::ERROR_DEVICE_LOST => Err(Error::DeviceLost),
        vk::ERROR_MEMORY_MAP_FAILED => Err(Error::MemoryMapFailed),
        vk::ERROR_LAYER_NOT_PRESENT => Err(Error::LayerNotPresent),
        vk::ERROR_EXTENSION_NOT_PRESENT => Err(Error::ExtensionNotPresent),
        vk::ERROR_FEATURE_NOT_PRESENT => Err(Error::FeatureNotPresent),
        vk::ERROR_INCOMPATIBLE_DRIVER => Err(Error::IncompatibleDriver),
        vk::ERROR_TOO_MANY_OBJECTS => Err(Error::TooManyObjects),
        vk::ERROR_FORMAT_NOT_SUPPORTED => Err(Error::FormatNotSupported),
        vk::ERROR_SURFACE_LOST_KHR => Err(Error::SurfaceLost),
        vk::ERROR_NATIVE_WINDOW_IN_USE_KHR => Err(Error::NativeWindowInUse),
        vk::SUBOPTIMAL_KHR => Ok(Success::Suboptimal),
        vk::ERROR_OUT_OF_DATE_KHR => Err(Error::OutOfDate),
        vk::ERROR_INCOMPATIBLE_DISPLAY_KHR => Err(Error::IncompatibleDisplay),
        vk::ERROR_VALIDATION_FAILED_EXT => Err(Error::ValidationFailed),
        vk::ERROR_OUT_OF_POOL_MEMORY_KHR => Err(Error::OutOfPoolMemory),
        c => unreachable!("Unexpected error code returned by Vulkan: {}", c)
    }
}
<|endoftext|>


<commit_before>use lapin::{executor::Executor, ConnectionProperties, Result};
use smol::Task;
use std::{future::Future, pin::Pin};

pub trait LapinSmolExt {
    fn with_smol(self) -> Self
    where
        Self: Sized;
}

impl LapinSmolExt for ConnectionProperties {
    fn with_smol(self) -> Self {
        self.with_executor(SmolExecutor)
    }
}

#[derive(Debug)]
struct SmolExecutor;

impl Executor for SmolExecutor {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        Task::spawn(f).detach();
        Ok(())
    }
}
<commit_msg>lapinou: implement Reactor<commit_after>use lapin::{
    executor::Executor,
    heartbeat::Heartbeat,
    reactor::{Reactor, ReactorBuilder, ReactorHandle, Slot},
    socket_state::{SocketEvent, SocketStateHandle},
    tcp::{TcpStream, TcpStreamWrapper},
    ConnectionProperties, Result,
};
use parking_lot::Mutex;
use smol::{Async, Task, Timer};
use std::{collections::HashMap, fmt, future::Future, pin::Pin, sync::Arc};

// ConnectionProperties extension

pub trait LapinSmolExt {
    fn with_smol(self) -> Self
    where
        Self: Sized;
}

impl LapinSmolExt for ConnectionProperties {
    fn with_smol(self) -> Self {
        self.with_executor(SmolExecutor)
            .with_reactor(SmolReactorBuilder)
    }
}

// Executor

#[derive(Debug)]
struct SmolExecutor;

impl Executor for SmolExecutor {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        Task::spawn(f).detach();
        Ok(())
    }
}

// Reactor

#[derive(Debug)]
struct SmolReactorBuilder;

#[derive(Debug)]
struct SmolReactor(SmolReactorHandle);

#[derive(Clone)]
struct SmolReactorHandle {
    heartbeat: Heartbeat,
    inner: Arc<Mutex<Inner>>,
}

#[derive(Default)]
struct Inner {
    slot: Slot,
    slots: HashMap<usize, (Arc<Async<TcpStreamWrapper>>, SocketStateHandle)>,
}

impl fmt::Debug for SmolReactorHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SmolReactorHandle").finish()
    }
}

impl Inner {
    fn register(
        &mut self,
        socket: Arc<Async<TcpStreamWrapper>>,
        socket_state: SocketStateHandle,
    ) -> Result<usize> {
        let slot = self.slot;
        self.slot += 1;
        self.slots.insert(slot, (socket, socket_state));
        Ok(slot)
    }
}

impl ReactorBuilder for SmolReactorBuilder {
    fn build(&self, heartbeat: Heartbeat) -> Result<Box<dyn Reactor + Send>> {
        Ok(Box::new(SmolReactor(SmolReactorHandle {
            heartbeat,
            inner: Arc::new(Mutex::new(Inner::default())),
        })))
    }
}

impl Reactor for SmolReactor {
    fn register(
        &mut self,
        socket: &mut TcpStream,
        socket_state: SocketStateHandle,
    ) -> Result<usize> {
        let socket = Arc::new(Async::new(unsafe { TcpStreamWrapper::new(socket) })?);
        let slot = self.0.inner.lock().register(socket, socket_state)?;
        self.0.poll_read(slot);
        self.0.poll_write(slot);
        Ok(slot)
    }

    fn handle(&self) -> Box<dyn ReactorHandle + Send> {
        Box::new(self.0.clone())
    }
}

impl ReactorHandle for SmolReactorHandle {
    fn start_heartbeat(&self) {
        Task::spawn(heartbeat(self.heartbeat.clone())).detach();
    }

    fn poll_read(&self, slot: usize) {
        if let Some((socket, socket_state)) = self.inner.lock().slots.get(&slot) {
            Task::spawn(poll_read(socket.clone(), socket_state.clone()))
                .unwrap()
                .detach();
        }
    }

    fn poll_write(&self, slot: usize) {
        if let Some((socket, socket_state)) = self.inner.lock().slots.get(&slot) {
            Task::spawn(poll_write(socket.clone(), socket_state.clone()))
                .unwrap()
                .detach();
        }
    }
}

async fn heartbeat(heartbeat: Heartbeat) {
    while let Ok(Some(timeout)) = heartbeat.poll_timeout() {
        Timer::after(timeout).await;
    }
}

async fn poll_read(
    socket: Arc<Async<TcpStreamWrapper>>,
    socket_state: SocketStateHandle,
) -> Result<()> {
    socket.with(|stream| stream.is_readable()).await?;
    socket_state.send(SocketEvent::Readable);
    Ok(())
}

async fn poll_write(
    socket: Arc<Async<TcpStreamWrapper>>,
    socket_state: SocketStateHandle,
) -> Result<()> {
    socket.with(|stream| stream.is_writable()).await?;
    socket_state.send(SocketEvent::Writable);
    Ok(())
}
<|endoftext|>


<commit_before>//! Request extractors
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use actix_http::error::Error;
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};

use crate::dev::Payload;
use crate::request::HttpRequest;

/// Trait implemented by types that can be extracted from request.
///
/// Types that implement this trait can be used with `Route` handlers.
pub trait FromRequest: Sized {
    /// The associated error which can be returned.
    type Error: Into<Error>;

    /// Future that resolves to a Self
    type Future: Future<Output = Result<Self, Self::Error>>;

    /// Configuration for this extractor
    type Config: Default + 'static;

    /// Convert request to a Self
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future;

    /// Convert request to a Self
    ///
    /// This method uses `Payload::None` as payload stream.
    fn extract(req: &HttpRequest) -> Self::Future {
        Self::from_request(req, &mut Payload::None)
    }

    /// Create and configure config instance.
    fn configure<F>(f: F) -> Self::Config
    where
        F: FnOnce(Self::Config) -> Self::Config,
    {
        f(Self::Config::default())
    }
}

/// Optionally extract a field from the request
///
/// If the FromRequest for T fails, return None rather than returning an error response
///
/// ## Example
///
/// ```rust
/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize;
/// use rand;
///
/// #[derive(Debug, Deserialize)]
/// struct Thing {
///     name: String
/// }
///
/// impl FromRequest for Thing {
///     type Error = Error;
///     type Future = Ready<Result<Self, Self::Error>>;
///     type Config = ();
///
///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
///         if rand::random() {
///             ok(Thing { name: "thingy".into() })
///         } else {
///             err(ErrorBadRequest("no luck"))
///         }
///
///     }
/// }
///
/// /// extract `Thing` from request
/// async fn index(supplied_thing: Option<Thing>) -> String {
///     match supplied_thing {
///         // Puns not intended
///         Some(thing) => format!("Got something: {:?}", thing),
///         None => format!("No thing!")
///     }
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/:first").route(
///             web::post().to(index))
///     );
/// }
/// ```
impl<T: 'static> FromRequest for Option<T>
where
    T: FromRequest,
    T::Future: 'static,
{
    type Config = T::Config;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Option<T>, Error>>;

    #[inline]
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        T::from_request(req, payload)
            .then(|r| match r {
                Ok(v) => ok(Some(v)),
                Err(e) => {
                    log::debug!("Error for Option<T> extractor: {}", e.into());
                    ok(None)
                }
            })
            .boxed_local()
    }
}

/// Optionally extract a field from the request or extract the Error if unsuccessful
///
/// If the `FromRequest` for T fails, inject Err into handler rather than returning an error response
///
/// ## Example
///
/// ```rust
/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize;
/// use rand;
///
/// #[derive(Debug, Deserialize)]
/// struct Thing {
///     name: String
/// }
///
/// impl FromRequest for Thing {
///     type Error = Error;
///     type Future = Ready<Result<Thing, Error>>;
///     type Config = ();
///
///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
///         if rand::random() {
///             ok(Thing { name: "thingy".into() })
///         } else {
///             err(ErrorBadRequest("no luck"))
///         }
///     }
/// }
///
/// /// extract `Thing` from request
/// async fn index(supplied_thing: Result<Thing>) -> String {
///     match supplied_thing {
///         Ok(thing) => format!("Got thing: {:?}", thing),
///         Err(e) => format!("Error extracting thing: {}", e)
///     }
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/:first").route(web::post().to(index))
///     );
/// }
/// ```
impl<T> FromRequest for Result<T, T::Error>
where
    T: FromRequest + 'static,
    T::Error: 'static,
    T::Future: 'static,
{
    type Config = T::Config;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Result<T, T::Error>, Error>>;

    #[inline]
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        T::from_request(req, payload)
            .then(|res| match res {
                Ok(v) => ok(Ok(v)),
                Err(e) => ok(Err(e)),
            })
            .boxed_local()
    }
}

#[doc(hidden)]
impl FromRequest for () {
    type Config = ();
    type Error = Error;
    type Future = Ready<Result<(), Error>>;

    fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
        ok(())
    }
}

macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {

    /// FromRequest implementation for tuple
    #[doc(hidden)]
    #[allow(unused_parens)]
    impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
    {
        type Error = Error;
        type Future = $fut_type<$($T),+>;
        type Config = ($($T::Config),+);

        fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
            $fut_type {
                items: <($(Option<$T>,)+)>::default(),
                futs: ($($T::from_request(req, payload),)+),
            }
        }
    }

    #[doc(hidden)]
    #[pin_project::pin_project]
    pub struct $fut_type<$($T: FromRequest),+> {
        items: ($(Option<$T>,)+),
        futs: ($($T::Future,)+),
    }

    impl<$($T: FromRequest),+> Future for $fut_type<$($T),+>
    {
        type Output = Result<($($T,)+), Error>;

        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.project();

            let mut ready = true;
            $(
                if this.items.$n.is_none() {
                    match unsafe { Pin::new_unchecked(&mut this.futs.$n) }.poll(cx) {
                        Poll::Ready(Ok(item)) => {
                            this.items.$n = Some(item);
                        }
                        Poll::Pending => ready = false,
                        Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
                    }
                }
            )+

                if ready {
                    Poll::Ready(Ok(
                        ($(this.items.$n.take().unwrap(),)+)
                    ))
                } else {
                    Poll::Pending
                }
        }
    }
});

#[rustfmt::skip]
mod m {
    use super::*;

tuple_from_req!(TupleFromRequest1, (0, A));
tuple_from_req!(TupleFromRequest2, (0, A), (1, B));
tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C));
tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D));
tuple_from_req!(TupleFromRequest5, (0, A), (1, B), (2, C), (3, D), (4, E));
tuple_from_req!(TupleFromRequest6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
tuple_from_req!(TupleFromRequest7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
tuple_from_req!(TupleFromRequest8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
tuple_from_req!(TupleFromRequest9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
tuple_from_req!(TupleFromRequest10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
}

#[cfg(test)]
mod tests {
    use actix_http::http::header;
    use bytes::Bytes;
    use serde_derive::Deserialize;

    use super::*;
    use crate::test::TestRequest;
    use crate::types::{Form, FormConfig};

    #[derive(Deserialize, Debug, PartialEq)]
    struct Info {
        hello: String,
    }

    #[actix_rt::test]
    async fn test_option() {
        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .data(FormConfig::default().limit(4096))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(r, None);

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"hello=world"))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(
            r,
            Some(Form(Info {
                hello: "world".into()
            }))
        );

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"bye=world"))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(r, None);
    }

    #[actix_rt::test]
    async fn test_result() {
        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "11")
        .set_payload(Bytes::from_static(b"hello=world"))
        .to_http_parts();

        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(
            r,
            Form(Info {
                hello: "world".into()
            })
        );

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"bye=world"))
        .to_http_parts();

        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert!(r.is_err());
    }
}
<commit_msg>Replace `Pin::new_unchecked` with #[pin_project] in `tuple_from_req!` (#1293)<commit_after>//! Request extractors
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

use actix_http::error::Error;
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};

use crate::dev::Payload;
use crate::request::HttpRequest;

/// Trait implemented by types that can be extracted from request.
///
/// Types that implement this trait can be used with `Route` handlers.
pub trait FromRequest: Sized {
    /// The associated error which can be returned.
    type Error: Into<Error>;

    /// Future that resolves to a Self
    type Future: Future<Output = Result<Self, Self::Error>>;

    /// Configuration for this extractor
    type Config: Default + 'static;

    /// Convert request to a Self
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future;

    /// Convert request to a Self
    ///
    /// This method uses `Payload::None` as payload stream.
    fn extract(req: &HttpRequest) -> Self::Future {
        Self::from_request(req, &mut Payload::None)
    }

    /// Create and configure config instance.
    fn configure<F>(f: F) -> Self::Config
    where
        F: FnOnce(Self::Config) -> Self::Config,
    {
        f(Self::Config::default())
    }
}

/// Optionally extract a field from the request
///
/// If the FromRequest for T fails, return None rather than returning an error response
///
/// ## Example
///
/// ```rust
/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize;
/// use rand;
///
/// #[derive(Debug, Deserialize)]
/// struct Thing {
///     name: String
/// }
///
/// impl FromRequest for Thing {
///     type Error = Error;
///     type Future = Ready<Result<Self, Self::Error>>;
///     type Config = ();
///
///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
///         if rand::random() {
///             ok(Thing { name: "thingy".into() })
///         } else {
///             err(ErrorBadRequest("no luck"))
///         }
///
///     }
/// }
///
/// /// extract `Thing` from request
/// async fn index(supplied_thing: Option<Thing>) -> String {
///     match supplied_thing {
///         // Puns not intended
///         Some(thing) => format!("Got something: {:?}", thing),
///         None => format!("No thing!")
///     }
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/:first").route(
///             web::post().to(index))
///     );
/// }
/// ```
impl<T: 'static> FromRequest for Option<T>
where
    T: FromRequest,
    T::Future: 'static,
{
    type Config = T::Config;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Option<T>, Error>>;

    #[inline]
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        T::from_request(req, payload)
            .then(|r| match r {
                Ok(v) => ok(Some(v)),
                Err(e) => {
                    log::debug!("Error for Option<T> extractor: {}", e.into());
                    ok(None)
                }
            })
            .boxed_local()
    }
}

/// Optionally extract a field from the request or extract the Error if unsuccessful
///
/// If the `FromRequest` for T fails, inject Err into handler rather than returning an error response
///
/// ## Example
///
/// ```rust
/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
/// use actix_web::error::ErrorBadRequest;
/// use futures::future::{ok, err, Ready};
/// use serde_derive::Deserialize;
/// use rand;
///
/// #[derive(Debug, Deserialize)]
/// struct Thing {
///     name: String
/// }
///
/// impl FromRequest for Thing {
///     type Error = Error;
///     type Future = Ready<Result<Thing, Error>>;
///     type Config = ();
///
///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
///         if rand::random() {
///             ok(Thing { name: "thingy".into() })
///         } else {
///             err(ErrorBadRequest("no luck"))
///         }
///     }
/// }
///
/// /// extract `Thing` from request
/// async fn index(supplied_thing: Result<Thing>) -> String {
///     match supplied_thing {
///         Ok(thing) => format!("Got thing: {:?}", thing),
///         Err(e) => format!("Error extracting thing: {}", e)
///     }
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/users/:first").route(web::post().to(index))
///     );
/// }
/// ```
impl<T> FromRequest for Result<T, T::Error>
where
    T: FromRequest + 'static,
    T::Error: 'static,
    T::Future: 'static,
{
    type Config = T::Config;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Result<T, T::Error>, Error>>;

    #[inline]
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        T::from_request(req, payload)
            .then(|res| match res {
                Ok(v) => ok(Ok(v)),
                Err(e) => ok(Err(e)),
            })
            .boxed_local()
    }
}

#[doc(hidden)]
impl FromRequest for () {
    type Config = ();
    type Error = Error;
    type Future = Ready<Result<(), Error>>;

    fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
        ok(())
    }
}

macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {

    // This module is a trick to get around the inability of
    // `macro_rules!` macros to make new idents. We want to make
    // a new `FutWrapper` struct for each distinct invocation of
    // this macro. Ideally, we would name it something like
    // `FutWrapper_$fut_type`, but this can't be done in a macro_rules
    // macro.
    //
    // Instead, we put everything in a module named `$fut_type`, thus allowing
    // us to use the name `FutWrapper` without worrying about conflicts.
    // This macro only exists to generate trait impls for tuples - these
    // are inherently global, so users don't have to care about this
    // weird trick.
    #[allow(non_snake_case)]
    mod $fut_type {

        // Bring everything into scope, so we don't need
        // redundant imports
        use super::*;

        /// A helper struct to allow us to pin-project through
        /// to individual fields
        #[pin_project::pin_project]
        struct FutWrapper<$($T: FromRequest),+>($(#[pin] $T::Future),+);

        /// FromRequest implementation for tuple
        #[doc(hidden)]
        #[allow(unused_parens)]
        impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
        {
            type Error = Error;
            type Future = $fut_type<$($T),+>;
            type Config = ($($T::Config),+);

            fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
                $fut_type {
                    items: <($(Option<$T>,)+)>::default(),
                    futs: FutWrapper($($T::from_request(req, payload),)+),
                }
            }
        }

        #[doc(hidden)]
        #[pin_project::pin_project]
        pub struct $fut_type<$($T: FromRequest),+> {
            items: ($(Option<$T>,)+),
            #[pin]
            futs: FutWrapper<$($T,)+>,
        }

        impl<$($T: FromRequest),+> Future for $fut_type<$($T),+>
        {
            type Output = Result<($($T,)+), Error>;

            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
                let mut this = self.project();

                let mut ready = true;
                $(
                    if this.items.$n.is_none() {
                        match this.futs.as_mut().project().$n.poll(cx) {
                            Poll::Ready(Ok(item)) => {
                                this.items.$n = Some(item);
                            }
                            Poll::Pending => ready = false,
                            Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
                        }
                    }
                )+

                    if ready {
                        Poll::Ready(Ok(
                            ($(this.items.$n.take().unwrap(),)+)
                        ))
                    } else {
                        Poll::Pending
                    }
            }
        }
    }
});

#[rustfmt::skip]
mod m {
    use super::*;

tuple_from_req!(TupleFromRequest1, (0, A));
tuple_from_req!(TupleFromRequest2, (0, A), (1, B));
tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C));
tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D));
tuple_from_req!(TupleFromRequest5, (0, A), (1, B), (2, C), (3, D), (4, E));
tuple_from_req!(TupleFromRequest6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
tuple_from_req!(TupleFromRequest7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
tuple_from_req!(TupleFromRequest8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
tuple_from_req!(TupleFromRequest9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
tuple_from_req!(TupleFromRequest10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
}

#[cfg(test)]
mod tests {
    use actix_http::http::header;
    use bytes::Bytes;
    use serde_derive::Deserialize;

    use super::*;
    use crate::test::TestRequest;
    use crate::types::{Form, FormConfig};

    #[derive(Deserialize, Debug, PartialEq)]
    struct Info {
        hello: String,
    }

    #[actix_rt::test]
    async fn test_option() {
        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .data(FormConfig::default().limit(4096))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(r, None);

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"hello=world"))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(
            r,
            Some(Form(Info {
                hello: "world".into()
            }))
        );

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"bye=world"))
        .to_http_parts();

        let r = Option::<Form<Info>>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert_eq!(r, None);
    }

    #[actix_rt::test]
    async fn test_result() {
        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "11")
        .set_payload(Bytes::from_static(b"hello=world"))
        .to_http_parts();

        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
            .await
            .unwrap()
            .unwrap();
        assert_eq!(
            r,
            Form(Info {
                hello: "world".into()
            })
        );

        let (req, mut pl) = TestRequest::with_header(
            header::CONTENT_TYPE,
            "application/x-www-form-urlencoded",
        )
        .header(header::CONTENT_LENGTH, "9")
        .set_payload(Bytes::from_static(b"bye=world"))
        .to_http_parts();

        let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
            .await
            .unwrap();
        assert!(r.is_err());
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc, Mutex,
};

use futures::{future::FutureExt, select_biased};
use tbot::{
    types::parameters::{self, WebPagePreviewState},
    Bot,
};
use tokio::{
    self,
    stream::StreamExt,
    sync::Notify,
    time::{self, delay_for, delay_queue::DelayQueue, Duration, Instant},
};

use crate::client::pull_feed;
use crate::data::{Database, Feed, FeedUpdate};
use crate::messages::{format_large_msg, Escape};

pub fn start(bot: Bot, db: Arc<Mutex<Database>>, min_interval: u32, max_interval: u32) {
    let mut queue = FetchQueue::new();
    // TODO: Don't use interval, it can accumulate ticks
    // replace it with delay_until
    let mut interval = time::interval_at(Instant::now(), Duration::from_secs(min_interval as u64));
    let throttle = Throttle::new(min_interval as usize);
    tokio::spawn(async move {
        loop {
            select_biased! {
                feed = queue.next().fuse() => {
                    let feed = feed.expect("unreachable");
                    let bot = bot.clone();
                    let db = db.clone();
                    let opportunity = throttle.acquire();
                    tokio::spawn(async move {
                        opportunity.wait().await;
                        if let Err(e) = fetch_and_push_updates(bot, db, feed).await {
                            crate::print_error(e);
                        }
                    });
                }
                _ = interval.tick().fuse() => {
                    let feeds = db.lock().unwrap().all_feeds();
                    for feed in feeds {
                        let feed_interval = cmp::min(
                            cmp::max(feed.ttl.map(|ttl| ttl * 60).unwrap_or(min_interval), min_interval),
                            max_interval,
                        ) as u64 - 1; // after -1, we can stagger with `interval`
                        queue.enqueue(feed, Duration::from_secs(feed_interval));
                    }
                }
            }
        }
    });
}

async fn fetch_and_push_updates(
    bot: Bot,
    db: Arc<Mutex<Database>>,
    feed: Feed,
) -> Result<(), tbot::errors::MethodCall> {
    let new_feed = match pull_feed(&feed.link).await {
        Ok(feed) => feed,
        Err(e) => {
            let down_time = db.lock().unwrap().get_or_update_down_time(&feed.link);
            if down_time.is_none() {
                // user unsubscribed while fetching the feed
                return Ok(());
            }
            // 5 days
            if down_time.unwrap().as_secs() > 5 * 24 * 60 * 60 {
                db.lock().unwrap().reset_down_time(&feed.link);
                let msg = format!(
                    "《{}》\
                     已经连续 5 天拉取出错 ({}),\
                     可能已经关闭, 请取消订阅",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&e.to_user_friendly())
                );
                push_updates(&bot, &db, feed.subscribers, parameters::Text::html(&msg)).await?;
            }
            return Ok(());
        }
    };

    let updates = db.lock().unwrap().update(&feed.link, new_feed);
    for update in updates {
        match update {
            FeedUpdate::Items(items) => {
                let msgs =
                    format_large_msg(format!("{}", Escape(&feed.title)), &items, |item| {
                        let title = item
                            .title
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.title);
                        let link = item
                            .link
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.link);
                        format!("{}", Escape(link), Escape(title))
                    });
                for msg in msgs {
                    push_updates(
                        &bot,
                        &db,
                        feed.subscribers.iter().copied(),
                        parameters::Text::html(&msg),
                    )
                    .await?;
                }
            }
            FeedUpdate::Title(new_title) => {
                let msg = format!(
                    "{} 已更名为 {}",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&new_title)
                );
                push_updates(
                    &bot,
                    &db,
                    feed.subscribers.iter().copied(),
                    parameters::Text::html(&msg),
                )
                .await?;
            }
        }
    }
    Ok(())
}

async fn push_updates<I: IntoIterator<Item = i64>>(
    bot: &Bot,
    db: &Arc<Mutex<Database>>,
    subscribers: I,
    msg: parameters::Text<'_>,
) -> Result<(), tbot::errors::MethodCall> {
    use tbot::errors::MethodCall;
    for mut subscriber in subscribers {
        'retry: for _ in 0..3 {
            match bot
                .send_message(tbot::types::chat::Id(subscriber), msg)
                .web_page_preview(WebPagePreviewState::Disabled)
                .call()
                .await
            {
                Err(MethodCall::RequestError { description, .. })
                    if chat_is_unavailable(&description) =>
                {
                    db.lock().unwrap().delete_subscriber(subscriber);
                }
                Err(MethodCall::RequestError {
                    migrate_to_chat_id: Some(new_chat_id),
                    ..
                }) => {
                    db.lock()
                        .unwrap()
                        .update_subscriber(subscriber, new_chat_id.0);
                    subscriber = new_chat_id.0;
                    continue 'retry;
                }
                Err(MethodCall::RequestError {
                    retry_after: Some(delay),
                    ..
                }) => {
                    time::delay_for(Duration::from_secs(delay)).await;
                    continue 'retry;
                }
                other => {
                    other?;
                }
            }
            break 'retry;
        }
    }
    Ok(())
}

pub fn chat_is_unavailable(s: &str) -> bool {
    s.contains("Forbidden")
        || s.contains("chat not found")
        || s.contains("have no rights")
        || s.contains("need administrator rights")
}

#[derive(Default)]
struct FetchQueue {
    feeds: HashMap<String, Feed>,
    notifies: DelayQueue<String>,
    wakeup: Notify,
}

impl FetchQueue {
    fn new() -> Self {
        Self::default()
    }

    fn enqueue(&mut self, feed: Feed, delay: Duration) -> bool {
        let exists = self.feeds.contains_key(&feed.link);
        if !exists {
            self.notifies.insert(feed.link.clone(), delay);
            self.feeds.insert(feed.link.clone(), feed);
            self.wakeup.notify();
        }
        !exists
    }

    async fn next(&mut self) -> Result<Feed, time::Error> {
        loop {
            if let Some(feed_id) = self.notifies.next().await {
                let feed = self.feeds.remove(feed_id?.get_ref()).unwrap();
                break Ok(feed);
            } else {
                self.wakeup.notified().await;
            }
        }
    }
}

struct Throttle {
    pieces: usize,
    counter: Arc<AtomicUsize>,
}

impl Throttle {
    fn new(pieces: usize) -> Self {
        Throttle {
            pieces,
            counter: Arc::new(AtomicUsize::new(0)),
        }
    }

    fn acquire(&self) -> Opportunity {
        Opportunity {
            n: self.counter.fetch_add(1, Ordering::AcqRel) % self.pieces,
            counter: self.counter.clone(),
        }
    }
}

#[must_use = "Don't lose your opportunity"]
struct Opportunity {
    n: usize,
    counter: Arc<AtomicUsize>,
}

impl Opportunity {
    async fn wait(&self) {
        delay_for(Duration::from_secs(self.n as u64)).await
    }
}

impl Drop for Opportunity {
    fn drop(&mut self) {
        self.counter.fetch_sub(1, Ordering::SeqCst);
    }
}
<commit_msg>Manually format code in macros<commit_after>use std::cmp;
use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc, Mutex,
};

use futures::{future::FutureExt, select_biased};
use tbot::{
    types::parameters::{self, WebPagePreviewState},
    Bot,
};
use tokio::{
    self,
    stream::StreamExt,
    sync::Notify,
    time::{self, delay_for, delay_queue::DelayQueue, Duration, Instant},
};

use crate::client::pull_feed;
use crate::data::{Database, Feed, FeedUpdate};
use crate::messages::{format_large_msg, Escape};

pub fn start(bot: Bot, db: Arc<Mutex<Database>>, min_interval: u32, max_interval: u32) {
    let mut queue = FetchQueue::new();
    // TODO: Don't use interval, it can accumulate ticks
    // replace it with delay_until
    let mut interval = time::interval_at(Instant::now(), Duration::from_secs(min_interval as u64));
    let throttle = Throttle::new(min_interval as usize);
    tokio::spawn(async move {
        loop {
            select_biased! {
                feed = queue.next().fuse() => {
                    let feed = feed.expect("unreachable");
                    let bot = bot.clone();
                    let db = db.clone();
                    let opportunity = throttle.acquire();
                    tokio::spawn(async move {
                        opportunity.wait().await;
                        if let Err(e) = fetch_and_push_updates(bot, db, feed).await {
                            crate::print_error(e);
                        }
                    });
                }
                _ = interval.tick().fuse() => {
                    let feeds = db.lock().unwrap().all_feeds();
                    for feed in feeds {
                        let feed_interval = cmp::min(
                            cmp::max(
                                feed.ttl.map(|ttl| ttl * 60).unwrap_or_default(),
                                min_interval,
                            ),
                            max_interval,
                        ) as u64 - 1; // after -1, we can stagger with `interval`
                        queue.enqueue(feed, Duration::from_secs(feed_interval));
                    }
                }
            }
        }
    });
}

async fn fetch_and_push_updates(
    bot: Bot,
    db: Arc<Mutex<Database>>,
    feed: Feed,
) -> Result<(), tbot::errors::MethodCall> {
    let new_feed = match pull_feed(&feed.link).await {
        Ok(feed) => feed,
        Err(e) => {
            let down_time = db.lock().unwrap().get_or_update_down_time(&feed.link);
            if down_time.is_none() {
                // user unsubscribed while fetching the feed
                return Ok(());
            }
            // 5 days
            if down_time.unwrap().as_secs() > 5 * 24 * 60 * 60 {
                db.lock().unwrap().reset_down_time(&feed.link);
                let msg = format!(
                    "《{}》\
                     已经连续 5 天拉取出错 ({}),\
                     可能已经关闭, 请取消订阅",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&e.to_user_friendly())
                );
                push_updates(&bot, &db, feed.subscribers, parameters::Text::html(&msg)).await?;
            }
            return Ok(());
        }
    };

    let updates = db.lock().unwrap().update(&feed.link, new_feed);
    for update in updates {
        match update {
            FeedUpdate::Items(items) => {
                let msgs =
                    format_large_msg(format!("{}", Escape(&feed.title)), &items, |item| {
                        let title = item
                            .title
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.title);
                        let link = item
                            .link
                            .as_ref()
                            .map(|s| s.as_str())
                            .unwrap_or_else(|| &feed.link);
                        format!("{}", Escape(link), Escape(title))
                    });
                for msg in msgs {
                    push_updates(
                        &bot,
                        &db,
                        feed.subscribers.iter().copied(),
                        parameters::Text::html(&msg),
                    )
                    .await?;
                }
            }
            FeedUpdate::Title(new_title) => {
                let msg = format!(
                    "{} 已更名为 {}",
                    Escape(&feed.link),
                    Escape(&feed.title),
                    Escape(&new_title)
                );
                push_updates(
                    &bot,
                    &db,
                    feed.subscribers.iter().copied(),
                    parameters::Text::html(&msg),
                )
                .await?;
            }
        }
    }
    Ok(())
}

async fn push_updates<I: IntoIterator<Item = i64>>(
    bot: &Bot,
    db: &Arc<Mutex<Database>>,
    subscribers: I,
    msg: parameters::Text<'_>,
) -> Result<(), tbot::errors::MethodCall> {
    use tbot::errors::MethodCall;
    for mut subscriber in subscribers {
        'retry: for _ in 0..3 {
            match bot
                .send_message(tbot::types::chat::Id(subscriber), msg)
                .web_page_preview(WebPagePreviewState::Disabled)
                .call()
                .await
            {
                Err(MethodCall::RequestError { description, .. })
                    if chat_is_unavailable(&description) =>
                {
                    db.lock().unwrap().delete_subscriber(subscriber);
                }
                Err(MethodCall::RequestError {
                    migrate_to_chat_id: Some(new_chat_id),
                    ..
                }) => {
                    db.lock()
                        .unwrap()
                        .update_subscriber(subscriber, new_chat_id.0);
                    subscriber = new_chat_id.0;
                    continue 'retry;
                }
                Err(MethodCall::RequestError {
                    retry_after: Some(delay),
                    ..
                }) => {
                    time::delay_for(Duration::from_secs(delay)).await;
                    continue 'retry;
                }
                other => {
                    other?;
                }
            }
            break 'retry;
        }
    }
    Ok(())
}

pub fn chat_is_unavailable(s: &str) -> bool {
    s.contains("Forbidden")
        || s.contains("chat not found")
        || s.contains("have no rights")
        || s.contains("need administrator rights")
}

#[derive(Default)]
struct FetchQueue {
    feeds: HashMap<String, Feed>,
    notifies: DelayQueue<String>,
    wakeup: Notify,
}

impl FetchQueue {
    fn new() -> Self {
        Self::default()
    }

    fn enqueue(&mut self, feed: Feed, delay: Duration) -> bool {
        let exists = self.feeds.contains_key(&feed.link);
        if !exists {
            self.notifies.insert(feed.link.clone(), delay);
            self.feeds.insert(feed.link.clone(), feed);
            self.wakeup.notify();
        }
        !exists
    }

    async fn next(&mut self) -> Result<Feed, time::Error> {
        loop {
            if let Some(feed_id) = self.notifies.next().await {
                let feed = self.feeds.remove(feed_id?.get_ref()).unwrap();
                break Ok(feed);
            } else {
                self.wakeup.notified().await;
            }
        }
    }
}

struct Throttle {
    pieces: usize,
    counter: Arc<AtomicUsize>,
}

impl Throttle {
    fn new(pieces: usize) -> Self {
        Throttle {
            pieces,
            counter: Arc::new(AtomicUsize::new(0)),
        }
    }

    fn acquire(&self) -> Opportunity {
        Opportunity {
            n: self.counter.fetch_add(1, Ordering::AcqRel) % self.pieces,
            counter: self.counter.clone(),
        }
    }
}

#[must_use = "Don't lose your opportunity"]
struct Opportunity {
    n: usize,
    counter: Arc<AtomicUsize>,
}

impl Opportunity {
    async fn wait(&self) {
        delay_for(Duration::from_secs(self.n as u64)).await
    }
}

impl Drop for Opportunity {
    fn drop(&mut self) {
        self.counter.fetch_sub(1, Ordering::SeqCst);
    }
}
<|endoftext|>


<commit_before>use crate::domain::model::configuration::ContainerConfig;
use crate::domain::model::update::UpdateOperation;
use crate::domain::model::{error::Error as ModelError, index::Index};
use crate::domain::ports::secondary::storage::Storage;
use async_trait::async_trait;
use common::document::ContainerDocument;
use futures::stream::Stream;
use tracing::{info, info_span};
use tracing_futures::Instrument;

#[async_trait]
pub trait GenerateIndex<'s> {
    /// Generate an index with provided stream of documents and publish it.
    async fn generate_index<D, S>(
        &self,
        config: &ContainerConfig,
        documents: S,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's;

    /// Same as generate_index but also applies a stream of updates to
    /// documents before publishing.
    async fn generate_and_update_index<D, S, U>(
        &self,
        config: &ContainerConfig,
        documents: S,
        operations: U,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's,
        U: Stream<Item = (String, UpdateOperation)> + Send + Sync + 's;
}

#[async_trait]
impl<'s, T> GenerateIndex<'s> for T
where
    T: Storage<'s> + Send + Sync + 'static,
{
    async fn generate_index<D, S>(
        &self,
        config: &ContainerConfig,
        documents: S,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's,
    {
        self.generate_and_update_index(config, documents, futures::stream::empty())
            .await
    }

    #[tracing::instrument(skip(self, config, documents, operations))]
    async fn generate_and_update_index<D, S, U>(
        &self,
        config: &ContainerConfig,
        documents: S,
        operations: U,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's,
        U: Stream<Item = (String, UpdateOperation)> + Send + Sync + 's,
    {
        // 1. We create the index
        // 2. We insert the document stream in that newly created index
        // 3. We update the documents with the input stream
        // 4. We publish the index
        // 5. We search for the newly created index to return it.
        let index = self
            .create_container(config)
            .instrument(info_span!("Create container"))
            .await
            .map_err(|err| ModelError::IndexCreation { source: err.into() })?;

        info!("Created new index: {:?}", index);

        let stats = self
            .insert_documents(index.name.clone(), documents)
            .instrument(info_span!("Insert documents"))
            .await
            .map_err(|err| ModelError::DocumentStreamInsertion { source: err.into() })?;

        info!("Index generation stats: {:?}", stats);

        let stats = self
            .update_documents(index.name.clone(), operations)
            .instrument(info_span!("Update documents"))
            .await
            .map_err(|err| ModelError::DocumentStreamUpdate { source: err.into() })?;

        info!("Index update stats: {:?}", stats);

        self.publish_index(index.clone(), config.visibility)
            .instrument(info_span!("Publish index"))
            .await
            .map_err(|err| ModelError::IndexPublication { source: err.into() })?;

        self.find_container(index.name.clone())
            .await
            .map_err(|err| ModelError::DocumentStreamInsertion { source: err.into() })?
            .ok_or(ModelError::ExpectedIndex { index: index.name })
    }
}
<commit_msg>generate_index: introduce index creation handle for insertion & updates<commit_after>use std::marker::PhantomData;

use crate::domain::model::configuration::ContainerConfig;
use crate::domain::model::update::UpdateOperation;
use crate::domain::model::{error::Error as ModelError, index::Index};
use crate::domain::ports::secondary::storage::Storage;
use async_trait::async_trait;
use common::document::ContainerDocument;
use futures::stream::Stream;
use tracing::{info, info_span};
use tracing_futures::Instrument;

#[async_trait(?Send)]
pub trait GenerateIndex<'s>
where
    Self: Storage<'s> + Send + Sync + 'static,
{
    /// Generate an index and provide an handle over it.
    async fn init_container<'a, D>(
        &'a self,
        config: &'a ContainerConfig,
    ) -> Result<ContainerGenerator<'a, D, Self>, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static;

    /// Generate an index with provided stream of documents and publish it.
    async fn generate_index<D, S>(
        &'s self,
        config: &'s ContainerConfig,
        documents: S,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's;
}

#[async_trait(?Send)]
impl<'s, T> GenerateIndex<'s> for T
where
    T: Storage<'s> + Send + Sync + Sized + 'static,
{
    #[tracing::instrument(skip(self, config))]
    async fn init_container<'a, D>(
        &'a self,
        config: &'a ContainerConfig,
    ) -> Result<ContainerGenerator<'a, D, Self>, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
    {
        let index = self
            .create_container(config)
            .instrument(info_span!("Create container"))
            .await
            .map_err(|err| ModelError::IndexCreation { source: err.into() })?;

        info!("Created new index: {:?}", index);

        Ok(ContainerGenerator {
            storage: self,
            config,
            index,
            _phantom: PhantomData::default(),
        })
    }

    async fn generate_index<D, S>(
        &'s self,
        config: &'s ContainerConfig,
        documents: S,
    ) -> Result<Index, ModelError>
    where
        D: ContainerDocument + Send + Sync + 'static,
        S: Stream<Item = D> + Send + Sync + 's,
    {
        self.init_container(config)
            .await?
            .insert_documents(documents)
            .await?
            .publish()
            .await
    }
}

/// Handle over an index which is beeing generated, it can be used to insert
/// or update documents.  When all documents are ready, `.publish()` must be
/// called to make the index available.
#[must_use]
pub struct ContainerGenerator<'a, D, T: ?Sized>
where
    D: ContainerDocument + Send + Sync + 'static,
{
    storage: &'a T,
    config: &'a ContainerConfig,
    index: Index,
    _phantom: PhantomData<*const D>,
}

impl<'a, 's, D, T> ContainerGenerator<'a, D, T>
where
    D: ContainerDocument + Send + Sync + 'static,
    T: Storage<'s> + Send + Sync,
{
    /// Insert new documents into the index
    #[tracing::instrument(skip(self, documents))]
    pub async fn insert_documents(
        self,
        documents: impl Stream<Item = D> + Send + Sync + 's,
    ) -> Result<ContainerGenerator<'a, D, T>, ModelError> {
        let stats = self
            .storage
            .insert_documents(self.index.name.clone(), documents)
            .await
            .map_err(|err| ModelError::DocumentStreamInsertion { source: err.into() })?;

        info!("Insertion stats: {:?}", stats);
        Ok(self)
    }

    /// Update documents that have already been inserted
    #[tracing::instrument(skip(self, updates))]
    pub async fn update_documents(
        self,
        updates: impl Stream<Item = (String, UpdateOperation)> + Send + Sync + 's,
    ) -> Result<ContainerGenerator<'a, D, T>, ModelError> {
        let stats = self
            .storage
            .update_documents(self.index.name.clone(), updates)
            .await
            .map_err(|err| ModelError::DocumentStreamUpdate { source: err.into() })?;

        info!("Update stats: {:?}", stats);
        Ok(self)
    }

    /// Publish the index, which consumes the handle
    #[tracing::instrument(skip(self))]
    pub async fn publish(self) -> Result<Index, ModelError> {
        self.storage
            .publish_index(self.index.clone(), self.config.visibility)
            .await
            .map_err(|err| ModelError::IndexPublication { source: err.into() })?;

        self.storage
            .find_container(self.index.name.clone())
            .await
            .map_err(|err| ModelError::DocumentStreamInsertion { source: err.into() })?
            .ok_or(ModelError::ExpectedIndex {
                index: self.index.name,
            })
    }
}
<|endoftext|>