<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//!Media module
//!
//!Provides functionality to upload images, GIFs and videos using Twitter Media API

use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;

use futures::{Future, Async, Poll};
use rustc_serialize::{json, base64};
use rustc_serialize::base64::{ToBase64};
use tokio_core::reactor::Timeout;

use common::*;
use error;
use error::Error::InvalidResponse;
use links;
use auth;

use mime;

/// A collection of convenience functions that return media types accepted by Twitter.
pub mod media_types {
    use mime::{self, Mime};

    /// PNG images.
    pub fn image_png() -> Mime {
        mime::IMAGE_PNG
    }

    /// JPG images.
    pub fn image_jpg() -> Mime {
        mime::IMAGE_JPEG
    }

    /// WEBP images.
    pub fn image_webp() -> Mime {
        "image/webp".parse().unwrap()
    }

    /// GIF images, both animated and static.
    pub fn image_gif() -> Mime {
        mime::IMAGE_GIF
    }

    /// MP4 videos.
    pub fn video_mp4() -> Mime {
        "video/mp4".parse().unwrap()
    }
}

///Media's upload progressing info.
#[derive(Debug, PartialEq)]
pub enum ProgressInfo {
    ///Video is pending for processing. Contains number of seconds after which to check.
    Pending(u64),
    ///Video is beeing processed. Contains number of seconds after which to check.
    InProgress(u64),
    ///Video's processing failed. Contains reason.
    Failed(error::MediaError),
    ///Video's processing is finished. Media can be used in other API calls.
    Success
}

impl FromJson for ProgressInfo {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        field_present!(input, state);
        let state: String = try!(field(input, "state"));

        match state.as_ref() {
            "pending" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::Pending(try!(field(input, "check_after_secs"))))
            },
            "in_progress" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::InProgress(try!(field(input, "check_after_secs"))))
            },
            "failed" => {
                field_present!(input, error);
                Ok(ProgressInfo::Failed(try!(field(input, "error"))))
            },
            "succeeded" => Ok(ProgressInfo::Success),
            state => Err(InvalidResponse("Unexpected progress info state", Some(state.to_string())))
        }
    }
}

///Represents media file that is uploaded on twitter.
pub struct Media {
    ///ID that can be used in API calls (e.g. attach to tweet).
    pub id: u64,
    ///Number of second the media can be used in other API calls.
    pub expires_after: u64,
    ///Progress information. If present determines whether Media can be used.
    pub progress: Option<ProgressInfo>
}

impl FromJson for Media {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("Tweet received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, media_id);

        Ok(Media {
            id: try!(field(input, "media_id")),
            //We can miss this field on failed upload in which case 0 is pretty reasonable value.
            expires_after: field(input, "expires_after_secs").unwrap_or(0),
            progress: try!(field(input, "processing_info"))
        })
    }
}

/// Represents the kinda of media that Twitter will accept.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MediaCategory {
    /// Static image. Four can be attached to a single tweet.
    Image,
    /// Animated GIF.
    Gif,
    /// Video.
    Video,
}

/// `Display` impl for `MediaCategory` so that `.to_string()` will return a string suitable for use
/// in an API call. This will turn the enum into `"tweet_image"`, `"tweet_gif"`, and
/// `"tweet_video"`.
impl ::std::fmt::Display for MediaCategory {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            MediaCategory::Image => write!(fmt, "tweet_image"),
            MediaCategory::Gif => write!(fmt, "tweet_gif"),
            MediaCategory::Video => write!(fmt, "tweet_video"),
        }
    }
}

/// A builder struct that allows you to build up parameters to a media upload before initiating it.
pub struct UploadBuilder<'a> {
    data: Cow<'a, [u8]>,
    media_type: mime::Mime,
    chunk_size: Option<usize>,
    category: Option<MediaCategory>,
}

impl<'a> UploadBuilder<'a> {
    /// Begins setting up a media upload call, with the given data and media type.
    ///
    /// For convenience functions to get known `media_type`s that Twitter will accept, see the
    /// [`media_types`] module.
    ///
    /// [`media_types`]: media_types/index.html
    pub fn new<V: Into<Cow<'a, [u8]>>>(data: V, media_type: mime::Mime) -> UploadBuilder<'a> {
        UploadBuilder {
            data: data.into(),
            media_type,
            chunk_size: None,
            category: None,
        }
    }

    /// Sets how many bytes to upload in one network call. By default this is set to 512 KiB.
    pub fn chunk_size(self, chunk_size: usize) -> Self {
        UploadBuilder {
            chunk_size: Some(chunk_size),
            ..self
        }
    }

    /// Sets the `media_category` sent to Twitter. When unset, it behaves as if you sent
    /// `MediaCategory::Image`.
    pub fn category(self, category: MediaCategory) -> Self {
        UploadBuilder {
            category: Some(category),
            ..self
        }
    }

    /// Collects the built-up parameters and begins the chunked upload.
    pub fn call(self, token: &auth::Token, handle: &Handle) -> UploadFuture<'a> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "INIT");
        add_param(&mut params, "total_bytes", self.data.len().to_string());
        add_param(&mut params, "media_type", self.media_type.to_string());

        if let Some(category) = self.category {
            add_param(&mut params, "media_category", category.to_string());
        }

        let req = auth::post(links::medias::UPLOAD, token, Some(&params));
        let loader = make_parsed_future(handle, req);
        UploadFuture {
            data: self.data,
            token: token.clone(),
            handle: handle.clone(),
            chunk_size: self.chunk_size.unwrap_or(1024 * 512), // 512 KiB default
            status: UploadInner::WaitingForInit(loader),
        }
    }
}

/// A `Future` that represents an in-progress media upload.
pub struct UploadFuture<'a> {
    data: Cow<'a, [u8]>,
    token: auth::Token,
    handle: Handle,
    chunk_size: usize,
    status: UploadInner,
}

/// The current status of an `UploadFuture`.
enum UploadInner {
    /// The `UploadFuture` is waiting to initialize the media upload session.
    WaitingForInit(FutureResponse<Media>),
    /// The `UploadFuture` is in the progress of uploading data.
    UploadingChunk(u64, usize, FutureResponse<()>),
    /// The `UploadFuture` is currently finalizing the media with Twitter.
    Finalizing(FutureResponse<Media>),
    /// The `UploadFuture` is waiting on Twitter to finish processing a video or gif.
    PostProcessing(u64, Timeout),
    /// The `UploadFuture` has completed, or has encountered an error.
    Invalid,
}

impl<'a> UploadFuture<'a> {
    fn get_chunk(&self, chunk_num: usize) -> Option<&[u8]> {
        let start = chunk_num * self.chunk_size;
        let end = (chunk_num + 1) * self.chunk_size;

        if start >= self.data.len() {
            None
        } else if end >= self.data.len() {
            Some(&self.data[start..])
        } else {
            Some(&self.data[start..end])
        }
    }

    fn append(&self, chunk_num: usize, media_id: u64) -> Option<FutureResponse<()>> {
        let mut chunk = self.get_chunk(chunk_num);
        if chunk.is_none() && chunk_num == 0 {
            chunk = Some(&[][..]);
        }

        if let Some(chunk) = chunk {
            let mut params = HashMap::new();

            let config = base64::Config {
                char_set: base64::CharacterSet::Standard,
                newline: base64::Newline::LF,
                pad: true,
                line_length: None,
            };

            add_param(&mut params, "command", "APPEND");
            add_param(&mut params, "media_id", media_id.to_string());
            add_param(&mut params, "media_data", chunk.to_base64(config));
            add_param(&mut params, "segment_index", chunk_num.to_string());

            let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
            Some(make_parsed_future(&self.handle, req))
        } else {
            None
        }
    }

    fn finalize(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "FINALIZE");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }

    fn status(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "STATUS");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::get(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }
}

impl<'a> Future for UploadFuture<'a> {
    type Item = Response<Media>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        use std::mem::replace;

        match replace(&mut self.status, UploadInner::Invalid) {
            UploadInner::WaitingForInit(mut init) => {
                match init.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::WaitingForInit(init);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        let id = media.id;
                        let loader = self.append(0, id).unwrap();
                        self.status = UploadInner::UploadingChunk(id, 0, loader);
                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::UploadingChunk(id, chunk_idx, mut upload) => {
                match upload.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(_)) => {
                        let chunk_idx = chunk_idx + 1;
                        if let Some(upload) = self.append(chunk_idx, id) {
                            self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        } else {
                            let loader = self.finalize(id);
                            self.status = UploadInner::Finalizing(loader);
                        }

                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::Finalizing(mut finalize) => {
                match finalize.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::Finalizing(finalize);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        if media.progress.is_none() || media.progress == Some(ProgressInfo::Success) {
                            return Ok(Async::Ready(media));
                        }

                        match media.progress {
                            Some(ProgressInfo::Pending(time)) |
                                Some(ProgressInfo::InProgress(time)) =>
                            {
                                let timer = try!(Timeout::new(Duration::from_secs(time), &self.handle));
                                self.status = UploadInner::PostProcessing(media.id, timer);
                                self.poll()
                            },
                            Some(ProgressInfo::Failed(ref err)) =>
                                Err(error::Error::MediaError(err.clone())),
                            None | Some(ProgressInfo::Success) => unreachable!(),
                        }
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::PostProcessing(id, mut timer) => {
                match timer.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::PostProcessing(id, timer);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(())) => {
                        let loader = self.status(id);
                        self.status = UploadInner::Finalizing(loader);
                        self.poll()
                    },
                    Err(e) => Err(e.into()),
                }
            },
            UploadInner::Invalid => Err(error::Error::FutureAlreadyCompleted),
        }
    }
}

#[cfg(test)]
mod tests {
    use common::FromJson;

    use super::Media;

    use std::fs::File;
    use std::io::Read;

    fn load_media(path: &str) -> Media {
        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();
        Media::from_str(&content).unwrap()
    }

    #[test]
    fn parse_media() {
        let media = load_media("src/media/media.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 86400);
    }

    #[test]
    fn parse_media_pending() {
        let media = load_media("src/media/media_pending.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 86400);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Pending(5)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_in_progress() {
        let media = load_media("src/media/media_in_progress.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 3595);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::InProgress(10)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_fail() {
        let media = load_media("src/media/media_fail.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 0);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Failed(error)) => assert_eq!(error, "InvalidMedia: Unsupported video format"),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }
}
<commit_msg>fix parse_media_fail test<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//!Media module
//!
//!Provides functionality to upload images, GIFs and videos using Twitter Media API

use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;

use futures::{Future, Async, Poll};
use rustc_serialize::{json, base64};
use rustc_serialize::base64::{ToBase64};
use tokio_core::reactor::Timeout;

use common::*;
use error;
use error::Error::InvalidResponse;
use links;
use auth;

use mime;

/// A collection of convenience functions that return media types accepted by Twitter.
pub mod media_types {
    use mime::{self, Mime};

    /// PNG images.
    pub fn image_png() -> Mime {
        mime::IMAGE_PNG
    }

    /// JPG images.
    pub fn image_jpg() -> Mime {
        mime::IMAGE_JPEG
    }

    /// WEBP images.
    pub fn image_webp() -> Mime {
        "image/webp".parse().unwrap()
    }

    /// GIF images, both animated and static.
    pub fn image_gif() -> Mime {
        mime::IMAGE_GIF
    }

    /// MP4 videos.
    pub fn video_mp4() -> Mime {
        "video/mp4".parse().unwrap()
    }
}

///Media's upload progressing info.
#[derive(Debug, PartialEq)]
pub enum ProgressInfo {
    ///Video is pending for processing. Contains number of seconds after which to check.
    Pending(u64),
    ///Video is beeing processed. Contains number of seconds after which to check.
    InProgress(u64),
    ///Video's processing failed. Contains reason.
    Failed(error::MediaError),
    ///Video's processing is finished. Media can be used in other API calls.
    Success
}

impl FromJson for ProgressInfo {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        field_present!(input, state);
        let state: String = try!(field(input, "state"));

        match state.as_ref() {
            "pending" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::Pending(try!(field(input, "check_after_secs"))))
            },
            "in_progress" => {
                field_present!(input, check_after_secs);
                Ok(ProgressInfo::InProgress(try!(field(input, "check_after_secs"))))
            },
            "failed" => {
                field_present!(input, error);
                Ok(ProgressInfo::Failed(try!(field(input, "error"))))
            },
            "succeeded" => Ok(ProgressInfo::Success),
            state => Err(InvalidResponse("Unexpected progress info state", Some(state.to_string())))
        }
    }
}

///Represents media file that is uploaded on twitter.
pub struct Media {
    ///ID that can be used in API calls (e.g. attach to tweet).
    pub id: u64,
    ///Number of second the media can be used in other API calls.
    pub expires_after: u64,
    ///Progress information. If present determines whether Media can be used.
    pub progress: Option<ProgressInfo>
}

impl FromJson for Media {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if !input.is_object() {
            return Err(InvalidResponse("Tweet received json that wasn't an object", Some(input.to_string())));
        }

        field_present!(input, media_id);

        Ok(Media {
            id: try!(field(input, "media_id")),
            //We can miss this field on failed upload in which case 0 is pretty reasonable value.
            expires_after: field(input, "expires_after_secs").unwrap_or(0),
            progress: try!(field(input, "processing_info"))
        })
    }
}

/// Represents the kinda of media that Twitter will accept.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MediaCategory {
    /// Static image. Four can be attached to a single tweet.
    Image,
    /// Animated GIF.
    Gif,
    /// Video.
    Video,
}

/// `Display` impl for `MediaCategory` so that `.to_string()` will return a string suitable for use
/// in an API call. This will turn the enum into `"tweet_image"`, `"tweet_gif"`, and
/// `"tweet_video"`.
impl ::std::fmt::Display for MediaCategory {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            MediaCategory::Image => write!(fmt, "tweet_image"),
            MediaCategory::Gif => write!(fmt, "tweet_gif"),
            MediaCategory::Video => write!(fmt, "tweet_video"),
        }
    }
}

/// A builder struct that allows you to build up parameters to a media upload before initiating it.
pub struct UploadBuilder<'a> {
    data: Cow<'a, [u8]>,
    media_type: mime::Mime,
    chunk_size: Option<usize>,
    category: Option<MediaCategory>,
}

impl<'a> UploadBuilder<'a> {
    /// Begins setting up a media upload call, with the given data and media type.
    ///
    /// For convenience functions to get known `media_type`s that Twitter will accept, see the
    /// [`media_types`] module.
    ///
    /// [`media_types`]: media_types/index.html
    pub fn new<V: Into<Cow<'a, [u8]>>>(data: V, media_type: mime::Mime) -> UploadBuilder<'a> {
        UploadBuilder {
            data: data.into(),
            media_type,
            chunk_size: None,
            category: None,
        }
    }

    /// Sets how many bytes to upload in one network call. By default this is set to 512 KiB.
    pub fn chunk_size(self, chunk_size: usize) -> Self {
        UploadBuilder {
            chunk_size: Some(chunk_size),
            ..self
        }
    }

    /// Sets the `media_category` sent to Twitter. When unset, it behaves as if you sent
    /// `MediaCategory::Image`.
    pub fn category(self, category: MediaCategory) -> Self {
        UploadBuilder {
            category: Some(category),
            ..self
        }
    }

    /// Collects the built-up parameters and begins the chunked upload.
    pub fn call(self, token: &auth::Token, handle: &Handle) -> UploadFuture<'a> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "INIT");
        add_param(&mut params, "total_bytes", self.data.len().to_string());
        add_param(&mut params, "media_type", self.media_type.to_string());

        if let Some(category) = self.category {
            add_param(&mut params, "media_category", category.to_string());
        }

        let req = auth::post(links::medias::UPLOAD, token, Some(&params));
        let loader = make_parsed_future(handle, req);
        UploadFuture {
            data: self.data,
            token: token.clone(),
            handle: handle.clone(),
            chunk_size: self.chunk_size.unwrap_or(1024 * 512), // 512 KiB default
            status: UploadInner::WaitingForInit(loader),
        }
    }
}

/// A `Future` that represents an in-progress media upload.
pub struct UploadFuture<'a> {
    data: Cow<'a, [u8]>,
    token: auth::Token,
    handle: Handle,
    chunk_size: usize,
    status: UploadInner,
}

/// The current status of an `UploadFuture`.
enum UploadInner {
    /// The `UploadFuture` is waiting to initialize the media upload session.
    WaitingForInit(FutureResponse<Media>),
    /// The `UploadFuture` is in the progress of uploading data.
    UploadingChunk(u64, usize, FutureResponse<()>),
    /// The `UploadFuture` is currently finalizing the media with Twitter.
    Finalizing(FutureResponse<Media>),
    /// The `UploadFuture` is waiting on Twitter to finish processing a video or gif.
    PostProcessing(u64, Timeout),
    /// The `UploadFuture` has completed, or has encountered an error.
    Invalid,
}

impl<'a> UploadFuture<'a> {
    fn get_chunk(&self, chunk_num: usize) -> Option<&[u8]> {
        let start = chunk_num * self.chunk_size;
        let end = (chunk_num + 1) * self.chunk_size;

        if start >= self.data.len() {
            None
        } else if end >= self.data.len() {
            Some(&self.data[start..])
        } else {
            Some(&self.data[start..end])
        }
    }

    fn append(&self, chunk_num: usize, media_id: u64) -> Option<FutureResponse<()>> {
        let mut chunk = self.get_chunk(chunk_num);
        if chunk.is_none() && chunk_num == 0 {
            chunk = Some(&[][..]);
        }

        if let Some(chunk) = chunk {
            let mut params = HashMap::new();

            let config = base64::Config {
                char_set: base64::CharacterSet::Standard,
                newline: base64::Newline::LF,
                pad: true,
                line_length: None,
            };

            add_param(&mut params, "command", "APPEND");
            add_param(&mut params, "media_id", media_id.to_string());
            add_param(&mut params, "media_data", chunk.to_base64(config));
            add_param(&mut params, "segment_index", chunk_num.to_string());

            let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
            Some(make_parsed_future(&self.handle, req))
        } else {
            None
        }
    }

    fn finalize(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "FINALIZE");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::post(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }

    fn status(&self, media_id: u64) -> FutureResponse<Media> {
        let mut params = HashMap::new();

        add_param(&mut params, "command", "STATUS");
        add_param(&mut params, "media_id", media_id.to_string());

        let req = auth::get(links::medias::UPLOAD, &self.token, Some(&params));
        make_parsed_future(&self.handle, req)
    }
}

impl<'a> Future for UploadFuture<'a> {
    type Item = Response<Media>;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        use std::mem::replace;

        match replace(&mut self.status, UploadInner::Invalid) {
            UploadInner::WaitingForInit(mut init) => {
                match init.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::WaitingForInit(init);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        let id = media.id;
                        let loader = self.append(0, id).unwrap();
                        self.status = UploadInner::UploadingChunk(id, 0, loader);
                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::UploadingChunk(id, chunk_idx, mut upload) => {
                match upload.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(_)) => {
                        let chunk_idx = chunk_idx + 1;
                        if let Some(upload) = self.append(chunk_idx, id) {
                            self.status = UploadInner::UploadingChunk(id, chunk_idx, upload);
                        } else {
                            let loader = self.finalize(id);
                            self.status = UploadInner::Finalizing(loader);
                        }

                        self.poll()
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::Finalizing(mut finalize) => {
                match finalize.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::Finalizing(finalize);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(media)) => {
                        if media.progress.is_none() || media.progress == Some(ProgressInfo::Success) {
                            return Ok(Async::Ready(media));
                        }

                        match media.progress {
                            Some(ProgressInfo::Pending(time)) |
                                Some(ProgressInfo::InProgress(time)) =>
                            {
                                let timer = try!(Timeout::new(Duration::from_secs(time), &self.handle));
                                self.status = UploadInner::PostProcessing(media.id, timer);
                                self.poll()
                            },
                            Some(ProgressInfo::Failed(ref err)) =>
                                Err(error::Error::MediaError(err.clone())),
                            None | Some(ProgressInfo::Success) => unreachable!(),
                        }
                    },
                    Err(e) => Err(e),
                }
            },
            UploadInner::PostProcessing(id, mut timer) => {
                match timer.poll() {
                    Ok(Async::NotReady) => {
                        self.status = UploadInner::PostProcessing(id, timer);
                        Ok(Async::NotReady)
                    },
                    Ok(Async::Ready(())) => {
                        let loader = self.status(id);
                        self.status = UploadInner::Finalizing(loader);
                        self.poll()
                    },
                    Err(e) => Err(e.into()),
                }
            },
            UploadInner::Invalid => Err(error::Error::FutureAlreadyCompleted),
        }
    }
}

#[cfg(test)]
mod tests {
    use common::FromJson;

    use super::Media;

    use std::fs::File;
    use std::io::Read;

    fn load_media(path: &str) -> Media {
        let mut file = File::open(path).unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();
        Media::from_str(&content).unwrap()
    }

    #[test]
    fn parse_media() {
        let media = load_media("src/media/media.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 86400);
    }

    #[test]
    fn parse_media_pending() {
        let media = load_media("src/media/media_pending.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 86400);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Pending(5)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_in_progress() {
        let media = load_media("src/media/media_in_progress.json");

        assert_eq!(media.id, 13);
        assert_eq!(media.expires_after, 3595);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::InProgress(10)) => (),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }

    #[test]
    fn parse_media_fail() {
        let media = load_media("src/media/media_fail.json");

        assert_eq!(media.id, 710511363345354753);
        assert_eq!(media.expires_after, 0);
        assert!(media.progress.is_some());

        match media.progress {
            Some(super::ProgressInfo::Failed(error)) =>
                assert_eq!(error, ::error::MediaError {
                    code: 1,
                    name: "InvalidMedia".to_string(),
                    message: "Unsupported video format".to_string(),
                }),
            other => assert!(false, format!("Unexpected value of progress={:?}", other))
        }
    }
}
<|endoftext|>


<commit_before>use std::managed;

/// Trait of dispatcher. A (collision) dispatcher typically takes two bodies in parameter and
/// return the corresponding narrow phase algorithm.
pub trait Dispatcher<B, NF> {
    /// Deduce the narrow phase from two bodies.
    fn dispatch(&self, @mut B, @mut B) -> NF;
    /// Tells whether a collision between two bodies can occur.
    fn is_valid(&self, @mut B, @mut B) -> bool;
}

/// Dispatcher which disallows dispatches between identical pointers. It has no result.
pub struct NoIdDispatcher<B>;

impl<B> Dispatcher<B, ()> for NoIdDispatcher<B> {
    fn dispatch(&self, _: @mut B, _: @mut B) -> () {
        ()
    }

    fn is_valid(&self, a: @mut B, b: @mut B) -> bool {
        !managed::mut_ptr_eq(a, b)
    }
}
<commit_msg>There is no reason for the dispatcher to take an @mut instead of a &.<commit_after>use std::borrow;

/// Trait of dispatcher. A (collision) dispatcher typically takes two bodies in parameter and
/// return the corresponding narrow phase algorithm.
pub trait Dispatcher<B, NF> {
    /// Deduce the narrow phase from two bodies.
    fn dispatch(&self, &B, &B) -> NF;
    /// Tells whether a collision between two bodies can occur.
    fn is_valid(&self, &B, &B) -> bool;
}

/// Dispatcher which disallows dispatches between identical pointers. It has no result.
pub struct NoIdDispatcher<B>;

impl<B> Dispatcher<B, ()> for NoIdDispatcher<B> {
    fn dispatch(&self, _: &B, _: &B) -> () {
        ()
    }

    fn is_valid(&self, a: &B, b: &B) -> bool {
        !borrow::ref_eq(a, b)
    }
}
<|endoftext|>


<commit_before>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
};

use dces::prelude::Entity;

use crate::{prelude::*, render::RenderContext2D, tree::Tree, utils::prelude::*};

use super::Layout;

/// Stacks visual the children widgets vertical or horizontal.
#[derive(Default)]
pub struct StackLayout {
    desired_size: RefCell<DirtySize>,
    old_alignment: Cell<(Alignment, Alignment)>,
}

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

impl Layout for StackLayout {
    fn measure(
        &self,
        render_context_2_d: &mut RenderContext2D,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,

        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> DirtySize {
        if *ecm
            .component_store()
            .get::<Visibility>("visibility", entity)
            .unwrap()
            == Visibility::Collapsed
        {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return *self.desired_size.borrow();
        }

        let horizontal_alignment: Alignment = *ecm
            .component_store()
            .get("horizontal_alignment", entity)
            .unwrap();
        let vertical_alignment: Alignment = *ecm
            .component_store()
            .get("vertical_alignment", entity)
            .unwrap();

        if horizontal_alignment != self.old_alignment.get().1
            || vertical_alignment != self.old_alignment.get().0
        {
            self.desired_size.borrow_mut().set_dirty(true);
        }

        let orientation: Orientation = *ecm.component_store().get("orientation", entity).unwrap();
        let mut desired_size: (f64, f64) = (0.0, 0.0);
        let nchildren = ecm.entity_store().children[&entity].len();
        let spacing = spacing(ecm, entity);

        for index in 0..nchildren {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let child_desired_size =
                    child_layout.measure(render_context_2_d, child, ecm, layouts, theme);

                let mut child_margin = {
                    if child_desired_size.width() > 0.0 && child_desired_size.height() > 0.0 {
                        *ecm.component_store()
                            .get::<Thickness>("margin", child)
                            .unwrap()
                    } else {
                        Thickness::default()
                    }
                };

                if spacing != 0.0 && nchildren > 1 {
                    apply_spacing(&mut child_margin, spacing, orientation, index, nchildren);
                }

                match orientation {
                    Orientation::Horizontal => {
                        desired_size.0 +=
                            child_desired_size.width() + child_margin.left() + child_margin.right();
                        desired_size.1 = desired_size.1.max(
                            child_desired_size.height()
                                + child_margin.top()
                                + child_margin.bottom(),
                        );
                    }
                    _ => {
                        desired_size.0 = desired_size.0.max(
                            child_desired_size.width() + child_margin.left() + child_margin.right(),
                        );
                        desired_size.1 += child_desired_size.height()
                            + child_margin.top()
                            + child_margin.bottom();
                    }
                }

                let dirty = child_desired_size.dirty() || self.desired_size.borrow().dirty();
                self.desired_size.borrow_mut().set_dirty(dirty);
            }
        }

        self.desired_size
            .borrow_mut()
            .set_size(desired_size.0, desired_size.1);
        *self.desired_size.borrow()
    }

    fn arrange(
        &self,
        render_context_2_d: &mut RenderContext2D,
        parent_size: (f64, f64),
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> (f64, f64) {
        if *ecm
            .component_store()
            .get::<Visibility>("visibility", entity)
            .unwrap()
            == Visibility::Collapsed
        {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return (0.0, 0.0);
        }

        if !self.desired_size.borrow().dirty() {
            return self.desired_size.borrow().size();
        }

        let horizontal_alignment: Alignment = *ecm
            .component_store()
            .get("horizontal_alignment", entity)
            .unwrap();
        let vertical_alignment: Alignment = *ecm
            .component_store()
            .get("vertical_alignment", entity)
            .unwrap();
        let margin: Thickness = *ecm.component_store().get("margin", entity).unwrap();
        let constraint = *ecm
            .component_store()
            .get::<Constraint>("constraint", entity)
            .unwrap();
        let orientation: Orientation = *ecm.component_store().get("orientation", entity).unwrap();
        let mut size_counter = 0.0;

        let size = constraint.perform((
            horizontal_alignment.align_measure(
                parent_size.0,
                self.desired_size.borrow().width(),
                margin.left(),
                margin.right(),
            ),
            vertical_alignment.align_measure(
                parent_size.1,
                self.desired_size.borrow().height(),
                margin.top(),
                margin.bottom(),
            ),
        ));

        if let Ok(bounds) = ecm
            .component_store_mut()
            .get_mut::<Rectangle>("bounds", entity)
        {
            bounds.set_width(size.0);
            bounds.set_height(size.1);
        }

        let available_size = size;
        let nchildren = ecm.entity_store().children[&entity].len();
        let spacing = spacing(ecm, entity);

        for index in 0..nchildren {
            let child = ecm.entity_store().children[&entity][index];

            let mut child_desired_size = (0.0, 0.0);
            if let Some(child_layout) = layouts.get(&child) {
                match orientation {
                    Orientation::Horizontal => {
                        // set width to 0.0 to shrink width of the child
                        child_desired_size = child_layout.arrange(
                            render_context_2_d,
                            (0.0, size.1),
                            child,
                            ecm,
                            layouts,
                            theme,
                        );
                    }
                    // set height to 0.0 to shrink height of the child
                    Orientation::Vertical => {
                        child_desired_size = child_layout.arrange(
                            render_context_2_d,
                            (size.0, 0.0),
                            child,
                            ecm,
                            layouts,
                            theme,
                        );
                    }
                }
            }

            let mut child_margin = {
                if child_desired_size.0 > 0.0 && child_desired_size.1 > 0.0 {
                    *ecm.component_store()
                        .get::<Thickness>("margin", child)
                        .unwrap()
                } else {
                    Thickness::default()
                }
            };

            if spacing != 0.0 && nchildren > 1 {
                apply_spacing(&mut child_margin, spacing, orientation, index, nchildren);
            }

            let child_horizontal_alignment: Alignment = *ecm
                .component_store()
                .get("horizontal_alignment", child)
                .unwrap();
            let child_vertical_alignment: Alignment = *ecm
                .component_store()
                .get("vertical_alignment", child)
                .unwrap();

            if let Ok(child_bounds) = ecm
                .component_store_mut()
                .get_mut::<Rectangle>("bounds", child)
            {
                match orientation {
                    Orientation::Horizontal => {
                        child_bounds.set_x(
                            size_counter
                                + child_horizontal_alignment.align_position(
                                    available_size.0,
                                    child_bounds.width(),
                                    child_margin.left(),
                                    child_margin.right(),
                                ),
                        );
                        child_bounds.set_y(child_vertical_alignment.align_position(
                            available_size.1,
                            child_bounds.height(),
                            child_margin.top(),
                            child_margin.bottom(),
                        ));
                        size_counter +=
                            child_bounds.width() + child_margin.left() + child_margin.right();
                    }
                    _ => {
                        child_bounds.set_x(child_horizontal_alignment.align_position(
                            available_size.0,
                            child_bounds.width(),
                            child_margin.left(),
                            child_margin.right(),
                        ));
                        child_bounds.set_y(
                            size_counter
                                + child_vertical_alignment.align_position(
                                    available_size.1,
                                    child_bounds.height(),
                                    child_margin.top(),
                                    child_margin.bottom(),
                                ),
                        );
                        size_counter +=
                            child_bounds.height() + child_margin.top() + child_margin.bottom();
                    }
                }
            }
        }

        self.desired_size.borrow_mut().set_dirty(false);
        size
    }
}

impl Into<Box<dyn Layout>> for StackLayout {
    fn into(self) -> Box<dyn Layout> {
        Box::new(self)
    }
}

/// Applies spacing to widgets in a stack, depending upon their position, and the orientation.
fn apply_spacing(margins: &mut Thickness, spacing: f64, orientation: Orientation, index: usize, nchildren: usize) {
    let start =  if index == 0 { 0.0 } else { spacing / 2.0 };
    let end = if index == nchildren - 1 { 0.0 } else { spacing / 2.0 };

    match orientation {
        Orientation::Vertical => {
            margins.top += start;
            margins.bottom += end;
        }
        Orientation::Horizontal => {
            margins.left += start;
            margins.right += end;
        }
    }
}

/// Fetch the spacing property, which is guaranteed to exist on a stack.
fn spacing(ecm: &mut EntityComponentManager<Tree, StringComponentStore>, entity: Entity) -> f64 {
    ecm.component_store()
        .get::<f64>("spacing", entity)
        .expect("stack layout missing spacing property")
        .clone()
}

#[cfg(test)]
mod tests {
    use orbtk_utils::{Thickness, Orientation};
    use std::iter;

    use super::apply_spacing;

    const NUM_WIDGETS: usize = 5;

    #[test]
    fn spacing_vertical() {
        let expected = iter::once(Thickness { left: 0.0, right: 0.0, top: 0.0, bottom: 2.0 })
            .chain(iter::repeat(Thickness { left: 0.0, right: 0.0, top: 2.0, bottom: 2.0 }).take(3))
            .chain(iter::once(Thickness { left: 0.0, right: 0.0, top: 2.0, bottom: 0.0 }));

        spacing(Orientation::Vertical, 4.0, expected);
    }

    #[test]
    fn spacing_horizontal() {
        let expected = iter::once(Thickness { left: 0.0, right: 4.0, top: 0.0, bottom: 0.0 })
            .chain(iter::repeat(Thickness { left: 4.0, right: 4.0, top: 0.0, bottom: 0.0 }).take(3))
            .chain(iter::once(Thickness { left: 4.0, right: 0.0, top: 0.0, bottom: 0.0 }));

        spacing(Orientation::Horizontal, 8.0, expected);
    }

    fn spacing(orientation: Orientation, spacing: f64, expected: impl Iterator<Item = Thickness>) {
        let inputs = (0..NUM_WIDGETS).map(|id| (id, Thickness::default()));

        for ((index, mut input), expected) in inputs.zip(expected) {
            apply_spacing(&mut input, spacing, orientation, index, NUM_WIDGETS);
            assert_eq!(input, expected);
        }
    }
}<commit_msg>refactor(layout): QoL improvement of stack layout module<commit_after>use std::{
    cell::{Cell, RefCell},
    collections::BTreeMap,
};

use dces::prelude::{Entity, Component};

use crate::{prelude::*, render::RenderContext2D, tree::Tree, utils::prelude::*};

use super::Layout;

/// Stacks visual the children widgets vertical or horizontal.
#[derive(Default)]
pub struct StackLayout {
    desired_size: RefCell<DirtySize>,
    old_alignment: Cell<(Alignment, Alignment)>,
}

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

    pub fn set_dirty(&self, dirty: bool) {
        self.desired_size.borrow_mut().set_dirty(dirty);
    }
}

impl Layout for StackLayout {
    fn measure(
        &self,
        render_context_2_d: &mut RenderContext2D,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> DirtySize {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            let mut desired = self.desired_size.borrow_mut();
            desired.set_size(0.0, 0.0);
            return desired.clone();
        }

        let halign: Alignment = component(ecm, entity, "horizontal_alignment");
        let valign: Alignment = component(ecm, entity, "vertical_alignment");
        let (old_valign, old_halign) = self.old_alignment.get();

        if halign != old_halign || valign != old_valign {
            self.set_dirty(true);
        }

        let orientation: Orientation = component(ecm, entity, "orientation");
        let mut dirty = false;
        let mut desired_size: (f64, f64) = (0.0, 0.0);

        let nchildren = ecm.entity_store().children[&entity].len();
        let spacing: f64 = component_or_default(ecm, entity, "spacing");

        for index in 0..nchildren {
            let child = ecm.entity_store().children[&entity][index];

            if let Some(child_layout) = layouts.get(&child) {
                let child_desired_size =
                    child_layout.measure(render_context_2_d, child, ecm, layouts, theme);

                let mut child_margin = {
                    if child_desired_size.width() > 0.0 && child_desired_size.height() > 0.0 {
                        component(ecm, child, "margin")
                    } else {
                        Thickness::default()
                    }
                };

                if spacing != 0.0 && nchildren > 1 {
                    apply_spacing(&mut child_margin, spacing, orientation, index, nchildren);
                }

                accumulate_desired_size(&mut desired_size, child_desired_size, child_margin, orientation);

                if child_desired_size.dirty() || self.desired_size.borrow().dirty() {
                    dirty = true;
                }
            }
        }

        self.set_dirty(dirty);

        let mut desired = self.desired_size.borrow_mut();
        desired.set_size(desired_size.0, desired_size.1);
        desired.clone()
    }

    fn arrange(
        &self,
        render_context_2_d: &mut RenderContext2D,
        parent_size: (f64, f64),
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        layouts: &BTreeMap<Entity, Box<dyn Layout>>,
        theme: &ThemeValue,
    ) -> (f64, f64) {
        if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
            self.desired_size.borrow_mut().set_size(0.0, 0.0);
            return (0.0, 0.0);
        }

        if !self.desired_size.borrow().dirty() {
            return self.desired_size.borrow().size();
        }

        let halign: Alignment = component(ecm, entity, "horizontal_alignment");
        let valign: Alignment = component(ecm, entity, "vertical_alignment");
        let margin: Thickness = component(ecm, entity, "margin");
        let constraint: Constraint = component(ecm, entity, "constraint");
        let orientation: Orientation = component(ecm, entity, "orientation");

        let mut size_counter = 0.0;

        let size = constraint.perform((
            halign.align_measure(
                parent_size.0,
                self.desired_size.borrow().width(),
                margin.left(),
                margin.right(),
            ),
            valign.align_measure(
                parent_size.1,
                self.desired_size.borrow().height(),
                margin.top(),
                margin.bottom(),
            ),
        ));

        if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
            bounds.set_width(size.0);
            bounds.set_height(size.1);
        }

        let available_size = size;
        let nchildren = ecm.entity_store().children[&entity].len();
        let spacing: f64 = component_or_default(ecm, entity, "spacing");

        for index in 0..nchildren {
            let child = ecm.entity_store().children[&entity][index];

            let mut child_desired_size = (0.0, 0.0);
            if let Some(child_layout) = layouts.get(&child) {
                let dimensions = match orientation {
                    // set width to 0.0 to shrink width of the child
                    Orientation::Horizontal => (0.0, size.1),

                    // set height to 0.0 to shrink height of the child
                    Orientation::Vertical => (size.0, 0.0)
                };

                child_desired_size = child_layout.arrange(
                    render_context_2_d,
                    dimensions,
                    child,
                    ecm,
                    layouts,
                    theme,
                );
            }

            let mut child_margin = {
                if child_desired_size.0 > 0.0 && child_desired_size.1 > 0.0 {
                    component(ecm, child, "margin")
                } else {
                    Thickness::default()
                }
            };

            if spacing != 0.0 && nchildren > 1 {
                apply_spacing(&mut child_margin, spacing, orientation, index, nchildren);
            }

            let child_halign: Alignment = component(ecm, child, "horizontal_alignment");
            let child_valign: Alignment = component(ecm, child, "vertical_alignment");

            if let Some(child_bounds) = component_try_mut::<Rectangle>(ecm, child, "bounds") {
                apply_arrangement(
                    child_bounds,
                    &mut size_counter,
                    child_margin,
                    (child_halign, child_valign),
                    orientation,
                    available_size
                );
            }
        }

        self.set_dirty(false);
        size
    }
}

impl From<StackLayout> for Box<dyn Layout> {
    fn from(layout: StackLayout) -> Self {
        Box::new(layout)
    }
}

fn apply_arrangement(
    bounds: &mut Rectangle,
    size_counter: &mut f64,
    margin: Thickness,
    alignment: (Alignment, Alignment),
    orientation: Orientation,
    available_size: (f64, f64)
) {
    let (xpos, ypos, size);

    match orientation {
        Orientation::Horizontal => {
            xpos = *size_counter + alignment.0.align_position(
                available_size.0,
                bounds.width(),
                margin.left(),
                margin.right(),
            );

            ypos = alignment.1.align_position(
                available_size.1,
                bounds.height(),
                margin.top(),
                margin.bottom(),
            );

            size = bounds.width() + margin.left() + margin.right();
        }
        _ => {
            xpos = alignment.0.align_position(
                available_size.0,
                bounds.width(),
                margin.left(),
                margin.right(),
            );

            ypos = *size_counter + alignment.1.align_position(
                available_size.1,
                bounds.height(),
                margin.top(),
                margin.bottom(),
            );

            size = bounds.height() + margin.top() + margin.bottom();
        }
    };

    bounds.set_x(xpos);
    bounds.set_y(ypos);
    *size_counter += size;
}

/// Applies spacing to widgets in a stack, depending upon their position, and the orientation.
fn apply_spacing(margins: &mut Thickness, spacing: f64, orientation: Orientation, index: usize, nchildren: usize) {
    let start =  if index == 0 { 0.0 } else { spacing / 2.0 };
    let end = if index == nchildren - 1 { 0.0 } else { spacing / 2.0 };

    match orientation {
        Orientation::Vertical => {
            margins.top += start;
            margins.bottom += end;
        }
        Orientation::Horizontal => {
            margins.left += start;
            margins.right += end;
        }
    }
}

fn accumulate_desired_size(
    desired_size: &mut (f64, f64),
    desired: DirtySize,
    margin: Thickness,
    orientation: Orientation
) {
    let width = desired.width() + margin.left() + margin.right();
    let height = desired.height() + margin.top() + margin.bottom();

    match orientation {
        Orientation::Horizontal => {
            desired_size.0 += width;
            desired_size.1 = height;
        }
        Orientation::Vertical => {
            desired_size.0 = width;
            desired_size.1 += height;
        }
    }
}

fn component<C: Component + Clone>(
    ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    entity: Entity,
    component: &str
) -> C {
    ecm.component_store().get::<C>(component, entity).unwrap().clone()
}

fn component_or_default<C: Component + Clone + Default>(
    ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    entity: Entity,
    component: &str
) -> C {
    ecm.component_store()
        .get::<C>(component, entity)
        .map(Clone::clone)
        .unwrap_or_default()
}

fn component_try_mut<'a, C: Component>(
    ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
    entity: Entity,
    component: &str
) -> Option<&'a mut C> {
    ecm.component_store_mut().get_mut::<C>(component, entity).ok()
}

#[cfg(test)]
mod tests {
    use orbtk_utils::{Thickness, Orientation};
    use std::iter;

    use super::apply_spacing;

    const NUM_WIDGETS: usize = 5;

    #[test]
    fn spacing_vertical() {
        let expected = iter::once(Thickness { left: 0.0, right: 0.0, top: 0.0, bottom: 2.0 })
            .chain(iter::repeat(Thickness { left: 0.0, right: 0.0, top: 2.0, bottom: 2.0 }).take(3))
            .chain(iter::once(Thickness { left: 0.0, right: 0.0, top: 2.0, bottom: 0.0 }));

        spacing(Orientation::Vertical, 4.0, expected);
    }

    #[test]
    fn spacing_horizontal() {
        let expected = iter::once(Thickness { left: 0.0, right: 4.0, top: 0.0, bottom: 0.0 })
            .chain(iter::repeat(Thickness { left: 4.0, right: 4.0, top: 0.0, bottom: 0.0 }).take(3))
            .chain(iter::once(Thickness { left: 4.0, right: 0.0, top: 0.0, bottom: 0.0 }));

        spacing(Orientation::Horizontal, 8.0, expected);
    }

    fn spacing(orientation: Orientation, spacing: f64, expected: impl Iterator<Item = Thickness>) {
        let inputs = (0..NUM_WIDGETS).map(|id| (id, Thickness::default()));

        for ((index, mut input), expected) in inputs.zip(expected) {
            apply_spacing(&mut input, spacing, orientation, index, NUM_WIDGETS);
            assert_eq!(input, expected);
        }
    }
}
<|endoftext|>


<commit_before>import libc::c_int;

export poll_event;

export event, quit_event, no_event;

type event_type = u8;

const noevent: u8 = 0u8;
const activeevent: u8 = 1u8;
const keydown: u8 = 2u8;
const keyup: u8 = 3u8;
const mousemotion: u8 = 4u8;
const mousebuttondown: u8 = 5u8;
const mousebuttonup: u8 = 6u8;
const joyaxismotion: u8 = 7u8;
const joyballmotion: u8 = 8u8;
const joyhatmotion: u8 = 9u8;
const joybuttondown: u8 = 10u8;
const joybuttonup: u8 = 11u8;
const quit: u8 = 12u8;
const syswmevent: u8 = 13u8;
const event_reserveda: u8 = 14u8;
const event_reservedb: u8 = 15u8;
const videoresize: u8 = 16u8;
const videoexpose: u8 = 17u8;
const event_reserved2: u8 = 18u8;
const event_reserved3: u8 = 19u8;
const event_reserved4: u8 = 20u8;
const event_reserved5: u8 = 21u8;
const event_reserved6: u8 = 22u8;
const event_reserved7: u8 = 23u8;
const event_userevent: u8 = 24u8;

type raw_event = {
    type_: event_type,
    // FIXME: Not sure exactly how big this needs to be
    event: (u64, u64, u64, u64, u64, u64, u64, u64,
            u64, u64, u64, u64, u64, u64, u64, u64,
            u64, u64, u64, u64, u64, u64, u64, u64)
};

enum event {
    keydown_event(*keyboard_event_),
    keyup_event(*keyboard_event_),
    quit_event,
    no_event
}

type quit_event_ = {
    type_: event_type
};

const SDL_RELEASED: u8 = 0u8;
const SDL_PRESSED: u8 = 1u8;

type keyboard_event_ = {
    type_: event_type,
    which: u8,
    state: u8,
    keysym: keyboard::keysym
};

fn null_event() -> raw_event {
    {
        type_: 0u8,
        event: (0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64,
                0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64,
                0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64)
    }
}

fn log_event(e: raw_event) {
    if e.type_ == noevent { ret }
    let name = if e.type_ == noevent { "none" }
    else if e.type_ == activeevent { "active" }
    else if e.type_ == keydown { "keydown" }
    else if e.type_ == keyup { "keyup" }
    else if e.type_ == mousemotion { "mousemotion" }
    else if e.type_ == mousebuttondown { "mousebuttondown" }
    else if e.type_ == mousebuttonup { "mousebuttonup" }
    else if e.type_ == quit { "quit" }
    else if e.type_ == syswmevent { "syswmevent" }
    else if e.type_ == videoresize { "videoresize" }
    else if e.type_ == videoexpose { "videoexpose" }
    else { "other" };
    #debug("event: %s", name);
}

fn poll_event(f: fn(event)) unsafe {
    let raw_event = null_event();
    let result = SDL::SDL_PollEvent(ptr::addr_of(raw_event));
    if result as int == 1 {
        let event_ptr = ptr::addr_of(raw_event.event);
        log_event(raw_event);
        if (raw_event.type_ == quit) {
            f(quit_event);
        } else if (raw_event.type_ == keydown) {
            f(keydown_event(unsafe::reinterpret_cast(event_ptr)));
        } else if (raw_event.type_ == keyup) {
            f(keyup_event(unsafe::reinterpret_cast(event_ptr)));
        } else {
            f(no_event);
        }
    } else {
        f(no_event);
    }
}

extern mod SDL {
    fn SDL_PollEvent(event: *raw_event) -> c_int;
}
<commit_msg>Exporting events for new resolve.<commit_after>import libc::c_int;

export poll_event;

export event, quit_event, no_event;

export keydown_event, keyup_event, quit_event, no_event;

type event_type = u8;

const noevent: u8 = 0u8;
const activeevent: u8 = 1u8;
const keydown: u8 = 2u8;
const keyup: u8 = 3u8;
const mousemotion: u8 = 4u8;
const mousebuttondown: u8 = 5u8;
const mousebuttonup: u8 = 6u8;
const joyaxismotion: u8 = 7u8;
const joyballmotion: u8 = 8u8;
const joyhatmotion: u8 = 9u8;
const joybuttondown: u8 = 10u8;
const joybuttonup: u8 = 11u8;
const quit: u8 = 12u8;
const syswmevent: u8 = 13u8;
const event_reserveda: u8 = 14u8;
const event_reservedb: u8 = 15u8;
const videoresize: u8 = 16u8;
const videoexpose: u8 = 17u8;
const event_reserved2: u8 = 18u8;
const event_reserved3: u8 = 19u8;
const event_reserved4: u8 = 20u8;
const event_reserved5: u8 = 21u8;
const event_reserved6: u8 = 22u8;
const event_reserved7: u8 = 23u8;
const event_userevent: u8 = 24u8;

type raw_event = {
    type_: event_type,
    // FIXME: Not sure exactly how big this needs to be
    event: (u64, u64, u64, u64, u64, u64, u64, u64,
            u64, u64, u64, u64, u64, u64, u64, u64,
            u64, u64, u64, u64, u64, u64, u64, u64)
};

enum event {
    keydown_event(*keyboard_event_),
    keyup_event(*keyboard_event_),
    quit_event,
    no_event
}

type quit_event_ = {
    type_: event_type
};

const SDL_RELEASED: u8 = 0u8;
const SDL_PRESSED: u8 = 1u8;

type keyboard_event_ = {
    type_: event_type,
    which: u8,
    state: u8,
    keysym: keyboard::keysym
};

fn null_event() -> raw_event {
    {
        type_: 0u8,
        event: (0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64,
                0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64,
                0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64, 0u64)
    }
}

fn log_event(e: raw_event) {
    if e.type_ == noevent { ret }
    let name = if e.type_ == noevent { "none" }
    else if e.type_ == activeevent { "active" }
    else if e.type_ == keydown { "keydown" }
    else if e.type_ == keyup { "keyup" }
    else if e.type_ == mousemotion { "mousemotion" }
    else if e.type_ == mousebuttondown { "mousebuttondown" }
    else if e.type_ == mousebuttonup { "mousebuttonup" }
    else if e.type_ == quit { "quit" }
    else if e.type_ == syswmevent { "syswmevent" }
    else if e.type_ == videoresize { "videoresize" }
    else if e.type_ == videoexpose { "videoexpose" }
    else { "other" };
    #debug("event: %s", name);
}

fn poll_event(f: fn(event)) unsafe {
    let raw_event = null_event();
    let result = SDL::SDL_PollEvent(ptr::addr_of(raw_event));
    if result as int == 1 {
        let event_ptr = ptr::addr_of(raw_event.event);
        log_event(raw_event);
        if (raw_event.type_ == quit) {
            f(quit_event);
        } else if (raw_event.type_ == keydown) {
            f(keydown_event(unsafe::reinterpret_cast(event_ptr)));
        } else if (raw_event.type_ == keyup) {
            f(keyup_event(unsafe::reinterpret_cast(event_ptr)));
        } else {
            f(no_event);
        }
    } else {
        f(no_event);
    }
}

extern mod SDL {
    fn SDL_PollEvent(event: *raw_event) -> c_int;
}
<|endoftext|>


<commit_before>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

use std::{fmt, ops, borrow};
use std::mem;

use glib;
use gst;

#[derive(Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct GstRc<T: 'static + MiniObject> {
    obj: &'static T,
}

impl<T: MiniObject> GstRc<T> {
    unsafe fn new(obj: &'static T, owned: bool) -> Self {
        if !owned {
            gst::gst_mini_object_ref(obj.as_ptr() as *mut gst::GstMiniObject);
        }

        GstRc { obj: obj }
    }

    pub unsafe fn from_owned_ptr(ptr: *const T::PtrType) -> Self {
        Self::new(T::from_ptr(ptr), true)
    }

    pub unsafe fn from_unowned_ptr(ptr: *const T::PtrType) -> Self {
        Self::new(T::from_ptr(ptr), false)
    }

    pub fn make_mut(&mut self) -> &mut T {
        unsafe {
            let ptr = self.obj.as_ptr();

            if self.is_writable() {
                return &mut *(self.obj as *const T as *mut T);
            }

            self.obj = T::from_ptr(gst::gst_mini_object_make_writable(ptr as
                                                                      *mut gst::GstMiniObject) as
                                   *const T::PtrType);
            assert!(self.is_writable());

            &mut *(self.obj as *const T as *mut T)
        }
    }

    pub fn get_mut(&mut self) -> Option<&mut T> {
        if self.is_writable() {
            Some(unsafe { &mut *(self.obj as *const T as *mut T) })
        } else {
            None
        }
    }

    pub fn copy(&self) -> Self {
        unsafe {
            GstRc::from_owned_ptr(gst::gst_mini_object_copy(self.obj.as_ptr() as
                                                            *const gst::GstMiniObject) as
                                  *const T::PtrType)
        }
    }

    fn is_writable(&self) -> bool {
        (unsafe {
             gst::gst_mini_object_is_writable(self.as_ptr() as *const gst::GstMiniObject)
         } == glib::GTRUE)
    }

    pub unsafe fn into_ptr(self) -> *const T::PtrType {
        let ptr = self.obj.as_ptr();
        mem::forget(self);

        ptr
    }
}

impl<T: MiniObject> ops::Deref for GstRc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.obj
    }
}

impl<T: MiniObject> AsRef<T> for GstRc<T> {
    fn as_ref(&self) -> &T {
        self.obj
    }
}

impl<T: MiniObject> borrow::Borrow<T> for GstRc<T> {
    fn borrow(&self) -> &T {
        self.obj
    }
}

// FIXME: Not generally possible because neither T nor ToOwned are defined here...
//impl<T: MiniObject> ToOwned for T {
//    type Owned = GstRc<T>;
//
//    fn to_owned(&self) -> GstRc<T> {
//        unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
//    }
//}

impl<T: MiniObject> Clone for GstRc<T> {
    fn clone(&self) -> GstRc<T> {
        unsafe { GstRc::from_unowned_ptr(self.obj.as_ptr()) }
    }
}

impl<T: MiniObject> Drop for GstRc<T> {
    fn drop(&mut self) {
        unsafe {
            gst::gst_mini_object_unref(self.obj.as_ptr() as *mut gst::GstMiniObject);
        }
    }
}

unsafe impl<T: MiniObject + Sync> Sync for GstRc<T> {}
unsafe impl<T: MiniObject + Send> Send for GstRc<T> {}

impl<T: MiniObject + fmt::Display> fmt::Display for GstRc<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.obj.fmt(f)
    }
}

pub unsafe trait MiniObject
    where Self: Sized
{
    type PtrType;

    unsafe fn as_ptr(&self) -> *const Self::PtrType {
        self as *const Self as *const Self::PtrType
    }

    unsafe fn as_mut_ptr(&self) -> *mut Self::PtrType {
        self as *const Self as *mut Self::PtrType
    }

    unsafe fn from_ptr<'a>(ptr: *const Self::PtrType) -> &'a Self {
        assert!(!ptr.is_null());
        &*(ptr as *const Self)
    }

    unsafe fn from_mut_ptr<'a>(ptr: *mut Self::PtrType) -> &'a mut Self {
        assert!(!ptr.is_null());
        &mut *(ptr as *mut Self)
    }
}

impl<'a, T: MiniObject> From<&'a T> for GstRc<T> {
    fn from(f: &'a T) -> GstRc<T> {
        unsafe { GstRc::from_unowned_ptr(f.as_ptr()) }
    }
}

impl<'a, T: MiniObject> From<&'a mut T> for GstRc<T> {
    fn from(f: &'a mut T) -> GstRc<T> {
        unsafe { GstRc::from_unowned_ptr(f.as_ptr()) }
    }
}
<commit_msg>Remove From<&T> impls for GstRc<T><commit_after>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

use std::{fmt, ops, borrow};
use std::mem;

use glib;
use gst;

#[derive(Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct GstRc<T: 'static + MiniObject> {
    obj: &'static T,
}

impl<T: MiniObject> GstRc<T> {
    unsafe fn new(obj: &'static T, owned: bool) -> Self {
        if !owned {
            gst::gst_mini_object_ref(obj.as_ptr() as *mut gst::GstMiniObject);
        }

        GstRc { obj: obj }
    }

    pub unsafe fn from_owned_ptr(ptr: *const T::PtrType) -> Self {
        Self::new(T::from_ptr(ptr), true)
    }

    pub unsafe fn from_unowned_ptr(ptr: *const T::PtrType) -> Self {
        Self::new(T::from_ptr(ptr), false)
    }

    pub fn make_mut(&mut self) -> &mut T {
        unsafe {
            let ptr = self.obj.as_ptr();

            if self.is_writable() {
                return &mut *(self.obj as *const T as *mut T);
            }

            self.obj = T::from_ptr(gst::gst_mini_object_make_writable(ptr as
                                                                      *mut gst::GstMiniObject) as
                                   *const T::PtrType);
            assert!(self.is_writable());

            &mut *(self.obj as *const T as *mut T)
        }
    }

    pub fn get_mut(&mut self) -> Option<&mut T> {
        if self.is_writable() {
            Some(unsafe { &mut *(self.obj as *const T as *mut T) })
        } else {
            None
        }
    }

    pub fn copy(&self) -> Self {
        unsafe {
            GstRc::from_owned_ptr(gst::gst_mini_object_copy(self.obj.as_ptr() as
                                                            *const gst::GstMiniObject) as
                                  *const T::PtrType)
        }
    }

    fn is_writable(&self) -> bool {
        (unsafe {
             gst::gst_mini_object_is_writable(self.as_ptr() as *const gst::GstMiniObject)
         } == glib::GTRUE)
    }

    pub unsafe fn into_ptr(self) -> *const T::PtrType {
        let ptr = self.obj.as_ptr();
        mem::forget(self);

        ptr
    }
}

impl<T: MiniObject> ops::Deref for GstRc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.obj
    }
}

impl<T: MiniObject> AsRef<T> for GstRc<T> {
    fn as_ref(&self) -> &T {
        self.obj
    }
}

impl<T: MiniObject> borrow::Borrow<T> for GstRc<T> {
    fn borrow(&self) -> &T {
        self.obj
    }
}

// FIXME: Not generally possible because neither T nor ToOwned are defined here...
//impl<T: MiniObject> ToOwned for T {
//    type Owned = GstRc<T>;
//
//    fn to_owned(&self) -> GstRc<T> {
//        unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
//    }
//}

impl<T: MiniObject> Clone for GstRc<T> {
    fn clone(&self) -> GstRc<T> {
        unsafe { GstRc::from_unowned_ptr(self.obj.as_ptr()) }
    }
}

impl<T: MiniObject> Drop for GstRc<T> {
    fn drop(&mut self) {
        unsafe {
            gst::gst_mini_object_unref(self.obj.as_ptr() as *mut gst::GstMiniObject);
        }
    }
}

unsafe impl<T: MiniObject + Sync> Sync for GstRc<T> {}
unsafe impl<T: MiniObject + Send> Send for GstRc<T> {}

impl<T: MiniObject + fmt::Display> fmt::Display for GstRc<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.obj.fmt(f)
    }
}

pub unsafe trait MiniObject
    where Self: Sized
{
    type PtrType;

    unsafe fn as_ptr(&self) -> *const Self::PtrType {
        self as *const Self as *const Self::PtrType
    }

    unsafe fn as_mut_ptr(&self) -> *mut Self::PtrType {
        self as *const Self as *mut Self::PtrType
    }

    unsafe fn from_ptr<'a>(ptr: *const Self::PtrType) -> &'a Self {
        assert!(!ptr.is_null());
        &*(ptr as *const Self)
    }

    unsafe fn from_mut_ptr<'a>(ptr: *mut Self::PtrType) -> &'a mut Self {
        assert!(!ptr.is_null());
        &mut *(ptr as *mut Self)
    }
}

<|endoftext|>


<commit_before>use futures_core::future::Future;
use futures_core::task::{LocalWaker, Poll, Wake, Waker};
use slab::Slab;
use std::fmt;
use std::cell::UnsafeCell;
use std::marker::Unpin;
use std::mem;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::task::local_waker_from_nonlocal;

/// A future that is cloneable and can be polled in multiple threads.
/// Use `Future::shared()` method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<Fut: Future> {
    inner: Arc<Inner<Fut>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            .finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
    Done,
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

const IDLE: usize = 0;
const POLLING: usize = 1;
const REPOLL: usize = 2;
const COMPLETE: usize = 3;
const POISONED: usize = 4;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Shared<Fut> {
        Shared {
            inner: Arc::new(Inner {
                future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
                notifier: Arc::new(Notifier {
                    state: AtomicUsize::new(IDLE),
                    wakers: Mutex::new(Some(Slab::new())),
                }),
            }),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// If any clone of this `Shared` has completed execution and there are still at least one
    /// clone of this `Shared` alive then, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<Fut::Output> {
        match self.inner.notifier.state.load(SeqCst) {
            COMPLETE => unsafe { self.clone_output() },
            POISONED => panic!("inner future panicked during poll"),
            _ => None,
        }
    }

    /// Registers the current task to receive a wakeup when `Inner` is awoken.
    fn set_waker(&mut self, lw: &LocalWaker) {
        // Acquire the lock first before checking COMPLETE to ensure there
        // isn't a race.
        let mut wakers = self.inner.notifier.wakers.lock().unwrap();
        let wakers = if let Some(wakers) = wakers.as_mut() {
            wakers
        } else {
            // The value is already available, so there's no need to set the waker.
            return
        };
        if self.waker_key == NULL_WAKER_KEY {
            self.waker_key = wakers.insert(Some(lw.clone().into_waker()));
        } else {
            let waker_slot = &mut wakers[self.waker_key];
            let needs_replacement = if let Some(old_waker) = waker_slot {
                // If there's still an unwoken waker in the slot, only replace
                // if the current one wouldn't wake the same task.
                !lw.will_wake_nonlocal(old_waker)
            } else {
                true
            };
            if needs_replacement {
                *waker_slot = Some(lw.clone().into_waker());
            }
        }
        debug_assert!(self.waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(&mut self) -> Fut::Output {
        if let Some(inner) = Arc::get_mut(&mut self.inner) {
            let output = mem::replace(
                &mut inner.future_or_output,
                UnsafeCell::new(FutureOrOutput::Done)
            );
            if let FutureOrOutput::Output(item) = output.into_inner() {
                return item;
            }
        }
        self.clone_output()
            .unwrap_or_else(|| panic!("Shared future polled again after completion"))
    }

    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn clone_output(&self) -> Option<Fut::Output> {
        match &*self.inner.future_or_output.get() {
            FutureOrOutput::Output(item) => Some(item.clone()),
            FutureOrOutput::Done => None,
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
}

impl<Fut: Future> Future for Shared<Fut>
where
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let this = &mut *self;

        this.set_waker(lw);

        match this.inner.notifier.state.compare_and_swap(IDLE, POLLING, SeqCst) {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING | REPOLL => {
                // Another task is currently polling, at this point we just want
                // to ensure that our task handle is currently registered

                return Poll::Pending;
            }
            COMPLETE => {
                return unsafe { Poll::Ready(this.take_or_clone_output()) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = local_waker_from_nonlocal(this.inner.notifier.clone());
        let lw = &waker;

        struct Reset<'a>(&'a AtomicUsize);

        impl<'a> Drop for Reset<'a> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let output = loop {
            let _reset = Reset(&this.inner.notifier.state);

            // Poll the future
            let res = unsafe {
                if let FutureOrOutput::Future(future) =
                    &mut *this.inner.future_or_output.get()
                {
                    Pin::new_unchecked(future).poll(lw)
                } else {
                    unreachable!()
                }
            };
            match res {
                Poll::Pending => {
                    // Not ready, try to release the handle
                    match this
                        .inner
                        .notifier
                        .state
                        .compare_and_swap(POLLING, IDLE, SeqCst)
                    {
                        POLLING => {
                            // Success
                            return Poll::Pending;
                        }
                        REPOLL => {
                            // Gotta poll again!
                            let prev =
                                this.inner.notifier.state.swap(POLLING, SeqCst);
                            assert_eq!(prev, REPOLL);
                        }
                        _ => unreachable!(),
                    }
                }
                Poll::Ready(output) => break output,
            }
        };

        if Arc::get_mut(&mut this.inner).is_some() {
            unsafe {
                *this.inner.future_or_output.get() = FutureOrOutput::Done;
            }
            return Poll::Ready(output);
        }

        let _reset = Reset(&this.inner.notifier.state);

        unsafe {
            *this.inner.future_or_output.get() =
                FutureOrOutput::Output(output.clone());
        }

        // Complete the future
        let mut lock = this.inner.notifier.wakers.lock().unwrap();
        this.inner.notifier.state.store(COMPLETE, SeqCst);
        let wakers = &mut lock.take().unwrap();
        for (_key, opt_waker) in wakers {
            if let Some(waker) = opt_waker.take() {
                waker.wake();
            }
        }
        Poll::Ready(output)
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Shared {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Ok(mut wakers) = self.inner.notifier.wakers.lock() {
                if let Some(wakers) = wakers.as_mut() {
                    wakers.remove(self.waker_key);
                }
            }
        }
    }
}

impl Wake for Notifier {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst);

        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}
<commit_msg>Ensure that Shared only creates one cloned output per completed instance<commit_after>use futures_core::future::Future;
use futures_core::task::{LocalWaker, Poll, Wake, Waker};
use slab::Slab;
use std::fmt;
use std::cell::UnsafeCell;
use std::marker::Unpin;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::task::local_waker_from_nonlocal;

/// A future that is cloneable and can be polled in multiple threads.
/// Use `Future::shared()` method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<Fut: Future> {
    inner: Option<Arc<Inner<Fut>>>,
    waker_key: usize,
}

struct Inner<Fut: Future> {
    future_or_output: UnsafeCell<FutureOrOutput<Fut>>,
    notifier: Arc<Notifier>,
}

struct Notifier {
    state: AtomicUsize,
    wakers: Mutex<Option<Slab<Option<Waker>>>>,
}

// The future itself is polled behind the `Arc`, so it won't be moved
// when `Shared` is moved.
impl<Fut: Future> Unpin for Shared<Fut> {}

impl<Fut: Future> fmt::Debug for Shared<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("inner", &self.inner)
            .field("waker_key", &self.waker_key)
            .finish()
    }
}

impl<Fut: Future> fmt::Debug for Inner<Fut> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            .finish()
    }
}

enum FutureOrOutput<Fut: Future> {
    Future(Fut),
    Output(Fut::Output),
}

unsafe impl<Fut> Send for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

unsafe impl<Fut> Sync for Inner<Fut>
where
    Fut: Future + Send,
    Fut::Output: Send + Sync,
{}

const IDLE: usize = 0;
const POLLING: usize = 1;
const REPOLL: usize = 2;
const COMPLETE: usize = 3;
const POISONED: usize = 4;

const NULL_WAKER_KEY: usize = usize::max_value();

impl<Fut: Future> Shared<Fut> {
    pub(super) fn new(future: Fut) -> Shared<Fut> {
        Shared {
            inner: Some(Arc::new(Inner {
                future_or_output: UnsafeCell::new(FutureOrOutput::Future(future)),
                notifier: Arc::new(Notifier {
                    state: AtomicUsize::new(IDLE),
                    wakers: Mutex::new(Some(Slab::new())),
                }),
            })),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Shared<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// If any clone of this `Shared` has completed execution and there are still at least one
    /// clone of this `Shared` alive then, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<Fut::Output> {
        match self.inner.as_ref().map(|inner| inner.notifier.state.load(SeqCst)) {
            Some(COMPLETE) => unsafe { Some(self.inner().clone_output()) },
            Some(POISONED) => panic!("inner future panicked during poll"),
            _ => None,
        }
    }

    fn inner(&self) -> &Arc<Inner<Fut>> {
        Self::inner_(&self.inner)
    }

    fn inner_(inner: &Option<Arc<Inner<Fut>>>) -> &Arc<Inner<Fut>> {
        inner.as_ref().expect("Shared future polled again after completion")
    }

    /// Registers the current task to receive a wakeup when `Inner` is awoken.
    fn set_waker(&mut self, lw: &LocalWaker) {
        // Acquire the lock first before checking COMPLETE to ensure there
        // isn't a race.
        let mut wakers = Self::inner_(&self.inner).notifier.wakers.lock().unwrap();
        let wakers = if let Some(wakers) = wakers.as_mut() {
            wakers
        } else {
            // The value is already available, so there's no need to set the waker.
            return
        };
        if self.waker_key == NULL_WAKER_KEY {
            self.waker_key = wakers.insert(Some(lw.clone().into_waker()));
        } else {
            let waker_slot = &mut wakers[self.waker_key];
            let needs_replacement = if let Some(old_waker) = waker_slot {
                // If there's still an unwoken waker in the slot, only replace
                // if the current one wouldn't wake the same task.
                !lw.will_wake_nonlocal(old_waker)
            } else {
                true
            };
            if needs_replacement {
                *waker_slot = Some(lw.clone().into_waker());
            }
        }
        debug_assert!(self.waker_key != NULL_WAKER_KEY);
    }

    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn take_or_clone_output(inner: Arc<Inner<Fut>>) -> Fut::Output {
        match Arc::try_unwrap(inner) {
            Ok(inner) => {
                match inner.future_or_output.into_inner() {
                    FutureOrOutput::Output(item) => item,
                    FutureOrOutput::Future(_) => unreachable!(),
                }
            }
            Err(inner) => inner.clone_output(),
        }
    }

}

impl<Fut> Inner<Fut>
where
    Fut: Future,
    Fut::Output: Clone,
{
    /// Safety: callers must first ensure that `self.inner.state`
    /// is `COMPLETE`
    unsafe fn clone_output(&self) -> Fut::Output {
        match &*self.future_or_output.get() {
            FutureOrOutput::Output(item) => item.clone(),
            FutureOrOutput::Future(_) => unreachable!(),
        }
    }
}

impl<Fut: Future> Future for Shared<Fut>
where
    Fut::Output: Clone,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let this = &mut *self;

        // Assert that we aren't completed
        this.inner();

        this.set_waker(lw);

        match this.inner().notifier.state.compare_and_swap(IDLE, POLLING, SeqCst) {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING | REPOLL => {
                // Another task is currently polling, at this point we just want
                // to ensure that our task handle is currently registered

                return Poll::Pending;
            }
            COMPLETE => {
                let inner = self.inner.take().unwrap();
                return unsafe { Poll::Ready(Self::take_or_clone_output(inner)) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        let waker = local_waker_from_nonlocal(this.inner().notifier.clone());
        let lw = &waker;

        struct Reset<'a>(&'a AtomicUsize);

        impl<'a> Drop for Reset<'a> {
            fn drop(&mut self) {
                use std::thread;

                if thread::panicking() {
                    self.0.store(POISONED, SeqCst);
                }
            }
        }

        let output = loop {
            let inner = this.inner();
            let _reset = Reset(&inner.notifier.state);

            // Poll the future
            let res = unsafe {
                if let FutureOrOutput::Future(future) =
                    &mut *inner.future_or_output.get()
                {
                    Pin::new_unchecked(future).poll(lw)
                } else {
                    unreachable!()
                }
            };
            match res {
                Poll::Pending => {
                    // Not ready, try to release the handle
                    match inner
                        .notifier
                        .state
                        .compare_and_swap(POLLING, IDLE, SeqCst)
                    {
                        POLLING => {
                            // Success
                            return Poll::Pending;
                        }
                        REPOLL => {
                            // Gotta poll again!
                            let prev = inner.notifier.state.swap(POLLING, SeqCst);
                            assert_eq!(prev, REPOLL);
                        }
                        _ => unreachable!(),
                    }
                }
                Poll::Ready(output) => break output,
            }
        };

        if Arc::get_mut(this.inner.as_mut().unwrap()).is_some() {
            this.inner.take();
            return Poll::Ready(output);
        }

        let inner = this.inner();

        let _reset = Reset(&inner.notifier.state);

        unsafe {
            *inner.future_or_output.get() =
                FutureOrOutput::Output(output.clone());
        }

        // Complete the future
        let mut lock = inner.notifier.wakers.lock().unwrap();
        inner.notifier.state.store(COMPLETE, SeqCst);
        let wakers = &mut lock.take().unwrap();
        for (_key, opt_waker) in wakers {
            if let Some(waker) = opt_waker.take() {
                waker.wake();
            }
        }

        Poll::Ready(output)
    }
}

impl<Fut> Clone for Shared<Fut>
where
    Fut: Future,
{
    fn clone(&self) -> Self {
        Shared {
            inner: self.inner.clone(),
            waker_key: NULL_WAKER_KEY,
        }
    }
}

impl<Fut> Drop for Shared<Fut>
where
    Fut: Future,
{
    fn drop(&mut self) {
        if self.waker_key != NULL_WAKER_KEY {
            if let Some(ref inner) = self.inner {
                if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                    if let Some(wakers) = wakers.as_mut() {
                        wakers.remove(self.waker_key);
                    }
                }
            }
        }
    }
}

impl Wake for Notifier {
    fn wake(arc_self: &Arc<Self>) {
        arc_self.state.compare_and_swap(POLLING, REPOLL, SeqCst);

        let wakers = &mut *arc_self.wakers.lock().unwrap();
        if let Some(wakers) = wakers.as_mut() {
            for (_key, opt_waker) in wakers {
                if let Some(waker) = opt_waker.take() {
                    waker.wake();
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::Mutex;

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeType(pub u32);

pub const ERROR: NodeType = NodeType(0);
pub const WHITESPACE: NodeType = NodeType(1);

#[derive(Clone, Copy)]
pub struct NodeTypeInfo {
    pub name: &'static str
}

impl NodeType {
    pub fn register(self, info: NodeTypeInfo) {
        NODE_INFO.lock().unwrap().insert(self, info);
    }

    pub fn name(self) -> &'static str {
        self.info().name
    }

    fn info(self, ) -> NodeTypeInfo {
        let m = NODE_INFO.lock().unwrap();
        m.get(&self)
            .expect("Unregistered NodeType")
            .clone()
    }
}

impl ::std::fmt::Debug for NodeType {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        f.write_str("NodeType(")?;
        let m = NODE_INFO.lock().unwrap();
        if let Some(n) = m.get(self) {
            f.write_str(n.name)?;
        } else {
            write!(f, "unregistered {}", self.0)?;
        }
        f.write_str(")")?;
        Ok(())
    }
}

lazy_static! {
    static ref NODE_INFO: Mutex<HashMap<NodeType, NodeTypeInfo>> = {
        let mut map = HashMap::new();
        map.insert(ERROR, NodeTypeInfo { name: "ERROR" });
        map.insert(WHITESPACE, NodeTypeInfo { name: "WHITESPACE" });
        Mutex::new(map)
    };
}
<commit_msg>Check that node is registered at most once<commit_after>use std::collections::HashMap;
use std::sync::Mutex;

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeType(pub u32);

pub const ERROR: NodeType = NodeType(0);
pub const WHITESPACE: NodeType = NodeType(1);

#[derive(Clone, Copy)]
pub struct NodeTypeInfo {
    pub name: &'static str
}

impl NodeType {
    pub fn register(self, info: NodeTypeInfo) {
        let prev = NODE_INFO.lock().unwrap().insert(self, info);
        assert!(prev.is_none(), "Node already registered: {:?}", self);
    }

    pub fn name(self) -> &'static str {
        self.info().name
    }

    fn info(self, ) -> NodeTypeInfo {
        let m = NODE_INFO.lock().unwrap();
        m.get(&self)
            .expect("Unregistered NodeType")
            .clone()
    }
}

impl ::std::fmt::Debug for NodeType {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        f.write_str("NodeType(")?;
        let m = NODE_INFO.lock().unwrap();
        if let Some(n) = m.get(self) {
            f.write_str(n.name)?;
        } else {
            write!(f, "unregistered {}", self.0)?;
        }
        f.write_str(")")?;
        Ok(())
    }
}

lazy_static! {
    static ref NODE_INFO: Mutex<HashMap<NodeType, NodeTypeInfo>> = {
        let mut map = HashMap::new();
        map.insert(ERROR, NodeTypeInfo { name: "ERROR" });
        map.insert(WHITESPACE, NodeTypeInfo { name: "WHITESPACE" });
        Mutex::new(map)
    };
}
<|endoftext|>