<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 libc;
use cell::UnsafeCell;

pub struct RWLock { inner: UnsafeCell<libc::pthread_rwlock_t> }

unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {}

impl RWLock {
    pub const fn new() -> RWLock {
        RWLock { inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER) }
    }
    #[inline]
    pub unsafe fn read(&self) {
        let r = libc::pthread_rwlock_rdlock(self.inner.get());

        // According to the pthread_rwlock_rdlock spec, this function **may**
        // fail with EDEADLK if a deadlock is detected. On the other hand
        // pthread mutexes will *never* return EDEADLK if they are initialized
        // as the "fast" kind (which ours always are). As a result, a deadlock
        // situation may actually return from the call to pthread_rwlock_rdlock
        // instead of blocking forever (as mutexes and Windows rwlocks do). Note
        // that not all unix implementations, however, will return EDEADLK for
        // their rwlocks.
        //
        // We roughly maintain the deadlocking behavior by panicking to ensure
        // that this lock acquisition does not succeed.
        if r == libc::EDEADLK {
            panic!("rwlock read lock would result in deadlock");
        } else {
            debug_assert_eq!(r, 0);
        }
    }
    #[inline]
    pub unsafe fn try_read(&self) -> bool {
        libc::pthread_rwlock_tryrdlock(self.inner.get()) == 0
    }
    #[inline]
    pub unsafe fn write(&self) {
        let r = libc::pthread_rwlock_wrlock(self.inner.get());
        // see comments above for why we check for EDEADLK
        if r == libc::EDEADLK {
            panic!("rwlock write lock would result in deadlock");
        } else {
            debug_assert_eq!(r, 0);
        }
    }
    #[inline]
    pub unsafe fn try_write(&self) -> bool {
        libc::pthread_rwlock_trywrlock(self.inner.get()) == 0
    }
    #[inline]
    pub unsafe fn read_unlock(&self) {
        let r = libc::pthread_rwlock_unlock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn write_unlock(&self) { self.read_unlock() }
    #[inline]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_rwlock_destroy(self.inner.get());
        // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a
        // rwlock that was just initialized with
        // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked)
        // or pthread_rwlock_init() is called, this behaviour no longer occurs.
        if cfg!(target_os = "dragonfly") {
            debug_assert!(r == 0 || r == libc::EINVAL);
        } else {
            debug_assert_eq!(r, 0);
        }
    }
}
<commit_msg>Don't allow pthread_rwlock_t to recursively lock itself<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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 libc;
use cell::UnsafeCell;

pub struct RWLock {
    inner: UnsafeCell<libc::pthread_rwlock_t>,
    write_locked: UnsafeCell<bool>,
}

unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {}

impl RWLock {
    pub const fn new() -> RWLock {
        RWLock {
            inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
            write_locked: UnsafeCell::new(false),
        }
    }
    #[inline]
    pub unsafe fn read(&self) {
        let r = libc::pthread_rwlock_rdlock(self.inner.get());

        // According to the pthread_rwlock_rdlock spec, this function **may**
        // fail with EDEADLK if a deadlock is detected. On the other hand
        // pthread mutexes will *never* return EDEADLK if they are initialized
        // as the "fast" kind (which ours always are). As a result, a deadlock
        // situation may actually return from the call to pthread_rwlock_rdlock
        // instead of blocking forever (as mutexes and Windows rwlocks do). Note
        // that not all unix implementations, however, will return EDEADLK for
        // their rwlocks.
        //
        // We roughly maintain the deadlocking behavior by panicking to ensure
        // that this lock acquisition does not succeed.
        //
        // We also check whether there this lock is already write locked. This
        // is only possible if it was write locked by the current thread and
        // the implementation allows recursive locking. The POSIX standard
        // doesn't require recursivly locking a rwlock to deadlock, but we can't
        // allow that because it could lead to aliasing issues.
        if r == libc::EDEADLK || *self.write_locked.get() {
            if r == 0 {
                self.raw_unlock();
            }
            panic!("rwlock read lock would result in deadlock");
        } else {
            debug_assert_eq!(r, 0);
        }
    }
    #[inline]
    pub unsafe fn try_read(&self) -> bool {
        let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
        if r == 0 && *self.write_locked.get() {
            self.raw_unlock();
            false
        } else {
            r == 0
        }
    }
    #[inline]
    pub unsafe fn write(&self) {
        let r = libc::pthread_rwlock_wrlock(self.inner.get());
        // see comments above for why we check for EDEADLK and write_locked
        if r == libc::EDEADLK || *self.write_locked.get() {
            if r == 0 {
                self.raw_unlock();
            }
            panic!("rwlock write lock would result in deadlock");
        } else {
            debug_assert_eq!(r, 0);
        }
        *self.write_locked.get() = true;
    }
    #[inline]
    pub unsafe fn try_write(&self) -> bool {
        let r = libc::pthread_rwlock_trywrlock(self.inner.get());
        if r == 0 && *self.write_locked.get() {
            self.raw_unlock();
            false
        } else if r == 0 {
            *self.write_locked.get() = true;
            true
        } else {
            false
        }
    }
    #[inline]
    unsafe fn raw_unlock(&self) {
        let r = libc::pthread_rwlock_unlock(self.inner.get());
        debug_assert_eq!(r, 0);
    }
    #[inline]
    pub unsafe fn read_unlock(&self) {
        debug_assert!(!*self.write_locked.get());
        self.raw_unlock();
    }
    #[inline]
    pub unsafe fn write_unlock(&self) {
        debug_assert!(*self.write_locked.get());
        *self.write_locked.get() = false;
        self.raw_unlock();
    }
    #[inline]
    pub unsafe fn destroy(&self) {
        let r = libc::pthread_rwlock_destroy(self.inner.get());
        // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a
        // rwlock that was just initialized with
        // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked)
        // or pthread_rwlock_init() is called, this behaviour no longer occurs.
        if cfg!(target_os = "dragonfly") {
            debug_assert!(r == 0 || r == libc::EINVAL);
        } else {
            debug_assert_eq!(r, 0);
        }
    }
}
<|endoftext|>


<commit_before>//! ESMTP features

use crate::transport::smtp::{
    authentication::Mechanism, error::Error, response::Response, util::XText,
};
use std::{
    collections::HashSet,
    fmt::{self, Display, Formatter},
    net::{Ipv4Addr, Ipv6Addr},
    result::Result,
};

/// Client identifier, the parameter to `EHLO`
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ClientId {
    /// A fully-qualified domain name
    Domain(String),
    /// An IPv4 address
    Ipv4(Ipv4Addr),
    /// An IPv6 address
    Ipv6(Ipv6Addr),
}

const LOCALHOST_CLIENT: ClientId = ClientId::Ipv4(Ipv4Addr::new(127, 0, 0, 1));

impl Default for ClientId {
    fn default() -> Self {
        // https://tools.ietf.org/html/rfc5321#section-4.1.4
        //
        // The SMTP client MUST, if possible, ensure that the domain parameter
        // to the EHLO command is a primary host name as specified for this
        // command in Section 2.3.5.  If this is not possible (e.g., when the
        // client's address is dynamically assigned and the client does not have
        // an obvious name), an address literal SHOULD be substituted for the
        // domain name.
        #[cfg(feature = "hostname")]
        {
            hostname::get()
                .ok()
                .and_then(|s| s.into_string().map(Self::Domain).ok())
                .unwrap_or(LOCALHOST_CLIENT)
        }
        #[cfg(not(feature = "hostname"))]
        LOCALHOST_CLIENT
    }
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Self::Domain(ref value) => f.write_str(value),
            Self::Ipv4(ref value) => write!(f, "[{}]", value),
            Self::Ipv6(ref value) => write!(f, "[IPv6:{}]", value),
        }
    }
}

impl ClientId {
    /// Creates a new `ClientId` from a fully qualified domain name
    pub fn new_domain(domain: String) -> Self {
        Self::Domain(domain)
    }
}

/// Supported ESMTP keywords
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152: https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531: https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487: https://tools.ietf.org/html/rfc2487
    StartTls,
    /// AUTH mechanism
    Authentication(Mechanism),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Extension::EightBitMime => f.write_str("8BITMIME"),
            Extension::SmtpUtfEight => f.write_str("SMTPUTF8"),
            Extension::StartTls => f.write_str("STARTTLS"),
            Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
        }
    }
}

/// Contains information about an SMTP server
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ServerInfo {
    /// Server name
    ///
    /// The name given in the server banner
    pub name: String,
    /// ESMTP features supported by the server
    ///
    /// It contains the features supported by the server and known by the `Extension` module.
    pub features: HashSet<Extension>,
}

impl Display for ServerInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let features = if self.features.is_empty() {
            "no supported features".to_string()
        } else {
            format!("{:?}", self.features)
        };
        write!(f, "{} with {}", self.name, features)
    }
}

impl ServerInfo {
    /// Parses a EHLO response to create a `ServerInfo`
    pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
        let name = match response.first_word() {
            Some(name) => name,
            None => return Err(Error::ResponseParsing("Could not read server name")),
        };

        let mut features: HashSet<Extension> = HashSet::new();

        for line in response.message.as_slice() {
            if line.is_empty() {
                continue;
            }

            let mut split = line.split_whitespace();
            match split.next().unwrap() {
                "8BITMIME" => {
                    features.insert(Extension::EightBitMime);
                }
                "SMTPUTF8" => {
                    features.insert(Extension::SmtpUtfEight);
                }
                "STARTTLS" => {
                    features.insert(Extension::StartTls);
                }
                "AUTH" => {
                    for mechanism in split {
                        match mechanism {
                            "PLAIN" => {
                                features.insert(Extension::Authentication(Mechanism::Plain));
                            }
                            "LOGIN" => {
                                features.insert(Extension::Authentication(Mechanism::Login));
                            }
                            "XOAUTH2" => {
                                features.insert(Extension::Authentication(Mechanism::Xoauth2));
                            }
                            _ => (),
                        }
                    }
                }
                _ => (),
            };
        }

        Ok(ServerInfo {
            name: name.to_string(),
            features,
        })
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_feature(&self, keyword: Extension) -> bool {
        self.features.contains(&keyword)
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool {
        self.features
            .contains(&Extension::Authentication(mechanism))
    }

    /// Gets a compatible mechanism from list
    pub fn get_auth_mechanism(&self, mechanisms: &[Mechanism]) -> Option<Mechanism> {
        for mechanism in mechanisms {
            if self.supports_auth_mechanism(*mechanism) {
                return Some(*mechanism);
            }
        }
        None
    }
}

/// A `MAIL FROM` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailParameter {
    /// `BODY` parameter
    Body(MailBodyParameter),
    /// `SIZE` parameter
    Size(usize),
    /// `SMTPUTF8` parameter
    SmtpUtfEight,
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for MailParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailParameter::Body(ref value) => write!(f, "BODY={}", value),
            MailParameter::Size(size) => write!(f, "SIZE={}", size),
            MailParameter::SmtpUtfEight => f.write_str("SMTPUTF8"),
            MailParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            MailParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailBodyParameter {
    /// `7BIT`
    SevenBit,
    /// `8BITMIME`
    EightBitMime,
}

impl Display for MailBodyParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailBodyParameter::SevenBit => f.write_str("7BIT"),
            MailBodyParameter::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RcptParameter {
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for RcptParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            RcptParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            RcptParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use crate::transport::smtp::{
        authentication::Mechanism,
        response::{Category, Code, Detail, Response, Severity},
    };
    use std::collections::HashSet;

    #[test]
    fn test_clientid_fmt() {
        assert_eq!(
            format!("{}", ClientId::new_domain("test".to_string())),
            "test".to_string()
        );
        assert_eq!(format!("{}", LOCALHOST_CLIENT), "[127.0.0.1]".to_string());
    }

    #[test]
    fn test_extension_fmt() {
        assert_eq!(
            format!("{}", Extension::EightBitMime),
            "8BITMIME".to_string()
        );
        assert_eq!(
            format!("{}", Extension::Authentication(Mechanism::Plain)),
            "AUTH PLAIN".to_string()
        );
    }

    #[test]
    fn test_serverinfo_fmt() {
        let mut eightbitmime = HashSet::new();
        assert!(eightbitmime.insert(Extension::EightBitMime));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: eightbitmime.clone(),
                }
            ),
            "name with {EightBitMime}".to_string()
        );

        let empty = HashSet::new();

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: empty,
                }
            ),
            "name with no supported features".to_string()
        );

        let mut plain = HashSet::new();
        assert!(plain.insert(Extension::Authentication(Mechanism::Plain)));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: plain.clone(),
                }
            ),
            "name with {Authentication(Plain)}".to_string()
        );
    }

    #[test]
    fn test_serverinfo() {
        let response = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features = HashSet::new();
        assert!(features.insert(Extension::EightBitMime));

        let server_info = ServerInfo {
            name: "me".to_string(),
            features,
        };

        assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info);

        assert!(server_info.supports_feature(Extension::EightBitMime));
        assert!(!server_info.supports_feature(Extension::StartTls));

        let response2 = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "AUTH PLAIN CRAM-MD5 XOAUTH2 OTHER".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features2 = HashSet::new();
        assert!(features2.insert(Extension::EightBitMime));
        assert!(features2.insert(Extension::Authentication(Mechanism::Plain),));
        assert!(features2.insert(Extension::Authentication(Mechanism::Xoauth2),));

        let server_info2 = ServerInfo {
            name: "me".to_string(),
            features: features2,
        };

        assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2);

        assert!(server_info2.supports_feature(Extension::EightBitMime));
        assert!(server_info2.supports_auth_mechanism(Mechanism::Plain));
        assert!(!server_info2.supports_feature(Extension::StartTls));
    }
}
<commit_msg>Mark ClientId::new as deprecated<commit_after>//! ESMTP features

use crate::transport::smtp::{
    authentication::Mechanism, error::Error, response::Response, util::XText,
};
use std::{
    collections::HashSet,
    fmt::{self, Display, Formatter},
    net::{Ipv4Addr, Ipv6Addr},
    result::Result,
};

/// Client identifier, the parameter to `EHLO`
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ClientId {
    /// A fully-qualified domain name
    Domain(String),
    /// An IPv4 address
    Ipv4(Ipv4Addr),
    /// An IPv6 address
    Ipv6(Ipv6Addr),
}

const LOCALHOST_CLIENT: ClientId = ClientId::Ipv4(Ipv4Addr::new(127, 0, 0, 1));

impl Default for ClientId {
    fn default() -> Self {
        // https://tools.ietf.org/html/rfc5321#section-4.1.4
        //
        // The SMTP client MUST, if possible, ensure that the domain parameter
        // to the EHLO command is a primary host name as specified for this
        // command in Section 2.3.5.  If this is not possible (e.g., when the
        // client's address is dynamically assigned and the client does not have
        // an obvious name), an address literal SHOULD be substituted for the
        // domain name.
        #[cfg(feature = "hostname")]
        {
            hostname::get()
                .ok()
                .and_then(|s| s.into_string().map(Self::Domain).ok())
                .unwrap_or(LOCALHOST_CLIENT)
        }
        #[cfg(not(feature = "hostname"))]
        LOCALHOST_CLIENT
    }
}

impl Display for ClientId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Self::Domain(ref value) => f.write_str(value),
            Self::Ipv4(ref value) => write!(f, "[{}]", value),
            Self::Ipv6(ref value) => write!(f, "[IPv6:{}]", value),
        }
    }
}

impl ClientId {
    #[deprecated(since = "0.10.0", note = "Please use the new_domain function instead")]
    /// Creates a new `ClientId` from a fully qualified domain name
    pub fn new(domain: String) -> Self {
        Self::Domain(domain)
    }

    /// Creates a new `ClientId` from a fully qualified domain name
    pub fn new_domain(domain: String) -> Self {
        Self::Domain(domain)
    }
}

/// Supported ESMTP keywords
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Extension {
    /// 8BITMIME keyword
    ///
    /// RFC 6152: https://tools.ietf.org/html/rfc6152
    EightBitMime,
    /// SMTPUTF8 keyword
    ///
    /// RFC 6531: https://tools.ietf.org/html/rfc6531
    SmtpUtfEight,
    /// STARTTLS keyword
    ///
    /// RFC 2487: https://tools.ietf.org/html/rfc2487
    StartTls,
    /// AUTH mechanism
    Authentication(Mechanism),
}

impl Display for Extension {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Extension::EightBitMime => f.write_str("8BITMIME"),
            Extension::SmtpUtfEight => f.write_str("SMTPUTF8"),
            Extension::StartTls => f.write_str("STARTTLS"),
            Extension::Authentication(ref mechanism) => write!(f, "AUTH {}", mechanism),
        }
    }
}

/// Contains information about an SMTP server
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ServerInfo {
    /// Server name
    ///
    /// The name given in the server banner
    pub name: String,
    /// ESMTP features supported by the server
    ///
    /// It contains the features supported by the server and known by the `Extension` module.
    pub features: HashSet<Extension>,
}

impl Display for ServerInfo {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let features = if self.features.is_empty() {
            "no supported features".to_string()
        } else {
            format!("{:?}", self.features)
        };
        write!(f, "{} with {}", self.name, features)
    }
}

impl ServerInfo {
    /// Parses a EHLO response to create a `ServerInfo`
    pub fn from_response(response: &Response) -> Result<ServerInfo, Error> {
        let name = match response.first_word() {
            Some(name) => name,
            None => return Err(Error::ResponseParsing("Could not read server name")),
        };

        let mut features: HashSet<Extension> = HashSet::new();

        for line in response.message.as_slice() {
            if line.is_empty() {
                continue;
            }

            let mut split = line.split_whitespace();
            match split.next().unwrap() {
                "8BITMIME" => {
                    features.insert(Extension::EightBitMime);
                }
                "SMTPUTF8" => {
                    features.insert(Extension::SmtpUtfEight);
                }
                "STARTTLS" => {
                    features.insert(Extension::StartTls);
                }
                "AUTH" => {
                    for mechanism in split {
                        match mechanism {
                            "PLAIN" => {
                                features.insert(Extension::Authentication(Mechanism::Plain));
                            }
                            "LOGIN" => {
                                features.insert(Extension::Authentication(Mechanism::Login));
                            }
                            "XOAUTH2" => {
                                features.insert(Extension::Authentication(Mechanism::Xoauth2));
                            }
                            _ => (),
                        }
                    }
                }
                _ => (),
            };
        }

        Ok(ServerInfo {
            name: name.to_string(),
            features,
        })
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_feature(&self, keyword: Extension) -> bool {
        self.features.contains(&keyword)
    }

    /// Checks if the server supports an ESMTP feature
    pub fn supports_auth_mechanism(&self, mechanism: Mechanism) -> bool {
        self.features
            .contains(&Extension::Authentication(mechanism))
    }

    /// Gets a compatible mechanism from list
    pub fn get_auth_mechanism(&self, mechanisms: &[Mechanism]) -> Option<Mechanism> {
        for mechanism in mechanisms {
            if self.supports_auth_mechanism(*mechanism) {
                return Some(*mechanism);
            }
        }
        None
    }
}

/// A `MAIL FROM` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailParameter {
    /// `BODY` parameter
    Body(MailBodyParameter),
    /// `SIZE` parameter
    Size(usize),
    /// `SMTPUTF8` parameter
    SmtpUtfEight,
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for MailParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailParameter::Body(ref value) => write!(f, "BODY={}", value),
            MailParameter::Size(size) => write!(f, "SIZE={}", size),
            MailParameter::SmtpUtfEight => f.write_str("SMTPUTF8"),
            MailParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            MailParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

/// Values for the `BODY` parameter to `MAIL FROM`
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MailBodyParameter {
    /// `7BIT`
    SevenBit,
    /// `8BITMIME`
    EightBitMime,
}

impl Display for MailBodyParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MailBodyParameter::SevenBit => f.write_str("7BIT"),
            MailBodyParameter::EightBitMime => f.write_str("8BITMIME"),
        }
    }
}

/// A `RCPT TO` extension parameter
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RcptParameter {
    /// Custom parameter
    Other {
        /// Parameter keyword
        keyword: String,
        /// Parameter value
        value: Option<String>,
    },
}

impl Display for RcptParameter {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            RcptParameter::Other {
                ref keyword,
                value: Some(ref value),
            } => write!(f, "{}={}", keyword, XText(value)),
            RcptParameter::Other {
                ref keyword,
                value: None,
            } => f.write_str(keyword),
        }
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use crate::transport::smtp::{
        authentication::Mechanism,
        response::{Category, Code, Detail, Response, Severity},
    };
    use std::collections::HashSet;

    #[test]
    fn test_clientid_fmt() {
        assert_eq!(
            format!("{}", ClientId::new_domain("test".to_string())),
            "test".to_string()
        );
        assert_eq!(format!("{}", LOCALHOST_CLIENT), "[127.0.0.1]".to_string());
    }

    #[test]
    fn test_extension_fmt() {
        assert_eq!(
            format!("{}", Extension::EightBitMime),
            "8BITMIME".to_string()
        );
        assert_eq!(
            format!("{}", Extension::Authentication(Mechanism::Plain)),
            "AUTH PLAIN".to_string()
        );
    }

    #[test]
    fn test_serverinfo_fmt() {
        let mut eightbitmime = HashSet::new();
        assert!(eightbitmime.insert(Extension::EightBitMime));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: eightbitmime.clone(),
                }
            ),
            "name with {EightBitMime}".to_string()
        );

        let empty = HashSet::new();

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: empty,
                }
            ),
            "name with no supported features".to_string()
        );

        let mut plain = HashSet::new();
        assert!(plain.insert(Extension::Authentication(Mechanism::Plain)));

        assert_eq!(
            format!(
                "{}",
                ServerInfo {
                    name: "name".to_string(),
                    features: plain.clone(),
                }
            ),
            "name with {Authentication(Plain)}".to_string()
        );
    }

    #[test]
    fn test_serverinfo() {
        let response = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features = HashSet::new();
        assert!(features.insert(Extension::EightBitMime));

        let server_info = ServerInfo {
            name: "me".to_string(),
            features,
        };

        assert_eq!(ServerInfo::from_response(&response).unwrap(), server_info);

        assert!(server_info.supports_feature(Extension::EightBitMime));
        assert!(!server_info.supports_feature(Extension::StartTls));

        let response2 = Response::new(
            Code::new(
                Severity::PositiveCompletion,
                Category::Unspecified4,
                Detail::One,
            ),
            vec![
                "me".to_string(),
                "AUTH PLAIN CRAM-MD5 XOAUTH2 OTHER".to_string(),
                "8BITMIME".to_string(),
                "SIZE 42".to_string(),
            ],
        );

        let mut features2 = HashSet::new();
        assert!(features2.insert(Extension::EightBitMime));
        assert!(features2.insert(Extension::Authentication(Mechanism::Plain),));
        assert!(features2.insert(Extension::Authentication(Mechanism::Xoauth2),));

        let server_info2 = ServerInfo {
            name: "me".to_string(),
            features: features2,
        };

        assert_eq!(ServerInfo::from_response(&response2).unwrap(), server_info2);

        assert!(server_info2.supports_feature(Extension::EightBitMime));
        assert!(server_info2.supports_auth_mechanism(Mechanism::Plain));
        assert!(!server_info2.supports_feature(Extension::StartTls));
    }
}
<|endoftext|>


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

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use font::FontTableTag;
use font::FractionalPixel;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;
use util::geometry::{Au, px_to_pt};

use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::{kCTFontDefaultOrientation};

use std::ptr;
use std::sync::Arc;

pub struct FontTable {
    data: CFData,
}

// Noncopyable.
impl Drop for FontTable {
    fn drop(&mut self) {}
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn with_buffer<F>(&self, blk: F) where F: FnOnce(*const u8, usize) {
        blk(self.data.bytes().as_ptr(), self.data.len() as usize);
    }
}

pub struct FontHandle {
    pub font_data: Arc<FontTemplateData>,
    pub ctfont: CTFont,
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont() {
            Some(ref ctfont) => {
                Ok(FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                })
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_weight::T::Weight100,
            v if v < 2.0 => font_weight::T::Weight200,
            v if v < 3.0 => font_weight::T::Weight300,
            v if v < 4.0 => font_weight::T::Weight400,
            v if v < 5.0 => font_weight::T::Weight500,
            v if v < 6.0 => font_weight::T::Weight600,
            v if v < 7.0 => font_weight::T::Weight700,
            v if v < 8.0 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId)
                        -> FractionalPixel {
        // TODO: Implement on mac
        0.0
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = Au::from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(|advance| Au::from_f64_px(advance))
                                  .unwrap_or(max_advance_width);

        let metrics =  FontMetrics {
            underline_size:   Au::from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: Au::from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          Au::from_pt(leading),
            x_height:         Au::from_pt(self.ctfont.x_height() as f64),
            em_size:          em_size,
            ascent:           Au::from_pt(ascent * scale),
            descent:          Au::from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        return metrics;
    }

    fn get_table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(FontTable::wrap(data))
        })
    }
}
<commit_msg>Remove no-op Drop implementation from FontTable<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/. */

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use font::FontTableTag;
use font::FractionalPixel;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;
use util::geometry::{Au, px_to_pt};

use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::{kCTFontDefaultOrientation};

use std::ptr;
use std::sync::Arc;

pub struct FontTable {
    data: CFData,
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn with_buffer<F>(&self, blk: F) where F: FnOnce(*const u8, usize) {
        blk(self.data.bytes().as_ptr(), self.data.len() as usize);
    }
}

pub struct FontHandle {
    pub font_data: Arc<FontTemplateData>,
    pub ctfont: CTFont,
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont() {
            Some(ref ctfont) => {
                Ok(FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                })
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_weight::T::Weight100,
            v if v < 2.0 => font_weight::T::Weight200,
            v if v < 3.0 => font_weight::T::Weight300,
            v if v < 4.0 => font_weight::T::Weight400,
            v if v < 5.0 => font_weight::T::Weight500,
            v if v < 6.0 => font_weight::T::Weight600,
            v if v < 7.0 => font_weight::T::Weight700,
            v if v < 8.0 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId)
                        -> FractionalPixel {
        // TODO: Implement on mac
        0.0
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = Au::from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(|advance| Au::from_f64_px(advance))
                                  .unwrap_or(max_advance_width);

        let metrics =  FontMetrics {
            underline_size:   Au::from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: Au::from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          Au::from_pt(leading),
            x_height:         Au::from_pt(self.ctfont.x_height() as f64),
            em_size:          em_size,
            ascent:           Au::from_pt(ascent * scale),
            descent:          Au::from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        return metrics;
    }

    fn get_table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(FontTable::wrap(data))
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use collections::HashSet;
use std::fmt;
use std::sync::Arc;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TimeStamp(u64);

const TSO_PHYSICAL_SHIFT_BITS: u64 = 18;

impl TimeStamp {
    /// Create a time stamp from physical and logical components.
    pub fn compose(physical: u64, logical: u64) -> TimeStamp {
        TimeStamp((physical << TSO_PHYSICAL_SHIFT_BITS) + logical)
    }

    pub const fn zero() -> TimeStamp {
        TimeStamp(0)
    }

    pub const fn max() -> TimeStamp {
        TimeStamp(std::u64::MAX)
    }

    pub const fn new(ts: u64) -> TimeStamp {
        TimeStamp(ts)
    }

    /// Extracts physical part of a timestamp, in milliseconds.
    pub fn physical(self) -> u64 {
        self.0 >> TSO_PHYSICAL_SHIFT_BITS
    }

    pub fn next(self) -> TimeStamp {
        TimeStamp(self.0 + 1)
    }

    pub fn prev(self) -> TimeStamp {
        TimeStamp(self.0 - 1)
    }

    pub fn incr(&mut self) -> &mut TimeStamp {
        self.0 += 1;
        self
    }

    pub fn decr(&mut self) -> &mut TimeStamp {
        self.0 -= 1;
        self
    }

    pub fn is_zero(self) -> bool {
        self.0 == 0
    }

    pub fn is_max(self) -> bool {
        self.0 == std::u64::MAX
    }

    pub fn into_inner(self) -> u64 {
        self.0
    }
}

impl From<u64> for TimeStamp {
    fn from(ts: u64) -> TimeStamp {
        TimeStamp(ts)
    }
}

impl From<&u64> for TimeStamp {
    fn from(ts: &u64) -> TimeStamp {
        TimeStamp(*ts)
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl slog::Value for TimeStamp {
    fn serialize(
        &self,
        record: &slog::Record,
        key: slog::Key,
        serializer: &mut dyn slog::Serializer,
    ) -> slog::Result {
        slog::Value::serialize(&self.0, record, key, serializer)
    }
}

const TS_SET_USE_VEC_LIMIT: usize = 8;

/// A hybrid immutable set for timestamps.
#[derive(Debug, Clone, PartialEq)]
pub enum TsSet {
    /// When the set is empty, avoid the useless cloning of Arc.
    Empty,
    /// `Vec` is suitable when the set is small or the set is barely used, and it doesn't worth
    /// converting a `Vec` into a `HashSet`.
    Vec(Arc<[TimeStamp]>),
    /// `Set` is suitable when there are many timestamps **and** it will be queried multiple times.
    Set(Arc<HashSet<TimeStamp>>),
}

impl Default for TsSet {
    #[inline]
    fn default() -> TsSet {
        TsSet::Empty
    }
}

impl TsSet {
    /// Create a `TsSet` from the given vec of timestamps. It will select the proper internal
    /// collection type according to the size.
    #[inline]
    pub fn new(ts: Vec<TimeStamp>) -> Self {
        if ts.is_empty() {
            TsSet::Empty
        } else if ts.len() <= TS_SET_USE_VEC_LIMIT {
            // If there are too few elements in `ts`, use Vec directly instead of making a Set.
            TsSet::Vec(ts.into())
        } else {
            TsSet::Set(Arc::new(ts.into_iter().collect()))
        }
    }

    pub fn from_u64s(ts: Vec<u64>) -> Self {
        // This conversion is safe because TimeStamp is a transparent wrapper over u64.
        let ts = unsafe { ::std::mem::transmute::<Vec<u64>, Vec<TimeStamp>>(ts) };
        Self::new(ts)
    }

    pub fn vec_from_u64s(ts: Vec<u64>) -> Self {
        // This conversion is safe because TimeStamp is a transparent wrapper over u64.
        let ts = unsafe { ::std::mem::transmute::<Vec<u64>, Vec<TimeStamp>>(ts) };
        Self::vec(ts)
    }

    /// Create a `TsSet` from the given vec of timestamps, but it will be forced to use `Vec` as the
    /// internal collection type. When it's sure that the set will be queried at most once, use this
    /// is better than `TsSet::new`, since both the querying on `Vec` and the conversion from `Vec`
    /// to `HashSet` is O(N).
    #[inline]
    pub fn vec(ts: Vec<TimeStamp>) -> Self {
        if ts.is_empty() {
            TsSet::Empty
        } else {
            TsSet::Vec(ts.into())
        }
    }

    /// Query whether the given timestamp is contained in the set.
    #[inline]
    pub fn contains(&self, ts: TimeStamp) -> bool {
        match self {
            TsSet::Empty => false,
            TsSet::Vec(vec) => vec.contains(&ts),
            TsSet::Set(set) => set.contains(&ts),
        }
    }
}

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

    #[test]
    fn test_ts() {
        let physical = 1568700549751;
        let logical = 108;
        let ts = TimeStamp::compose(physical, logical);
        assert_eq!(ts, 411225436913926252.into());

        let extracted_physical = ts.physical();
        assert_eq!(extracted_physical, physical);
    }

    #[test]
    fn test_split_ts() {
        let k = b"k";
        let ts = TimeStamp(123);
        assert!(Key::split_on_ts_for(k).is_err());
        let enc = Key::from_encoded_slice(k).append_ts(ts);
        let res = Key::split_on_ts_for(enc.as_encoded()).unwrap();
        assert_eq!(res, (k.as_ref(), ts));
    }

    #[test]
    fn test_ts_set() {
        let s = TsSet::new(vec![]);
        assert_eq!(s, TsSet::Empty);
        assert!(!s.contains(1.into()));

        let s = TsSet::vec(vec![]);
        assert_eq!(s, TsSet::Empty);

        let s = TsSet::from_u64s(vec![1, 2]);
        assert_eq!(s, TsSet::Vec(vec![1.into(), 2.into()].into()));
        assert!(s.contains(1.into()));
        assert!(s.contains(2.into()));
        assert!(!s.contains(3.into()));

        let s2 = TsSet::vec(vec![1.into(), 2.into()]);
        assert_eq!(s2, s);

        let big_ts_list: Vec<TimeStamp> =
            (0..=TS_SET_USE_VEC_LIMIT as u64).map(Into::into).collect();
        let s = TsSet::new(big_ts_list.clone());
        assert_eq!(
            s,
            TsSet::Set(Arc::new(big_ts_list.clone().into_iter().collect()))
        );
        assert!(s.contains(1.into()));
        assert!(s.contains((TS_SET_USE_VEC_LIMIT as u64).into()));
        assert!(!s.contains((TS_SET_USE_VEC_LIMIT as u64 + 1).into()));

        let s = TsSet::vec(big_ts_list.clone());
        assert_eq!(s, TsSet::Vec(big_ts_list.into()));
    }
}
<commit_msg>txn_types: Check overflow in timestamp computation (#9777)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use collections::HashSet;
use std::fmt;
use std::sync::Arc;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct TimeStamp(u64);

const TSO_PHYSICAL_SHIFT_BITS: u64 = 18;

impl TimeStamp {
    /// Create a time stamp from physical and logical components.
    pub fn compose(physical: u64, logical: u64) -> TimeStamp {
        TimeStamp((physical << TSO_PHYSICAL_SHIFT_BITS) + logical)
    }

    pub const fn zero() -> TimeStamp {
        TimeStamp(0)
    }

    pub const fn max() -> TimeStamp {
        TimeStamp(std::u64::MAX)
    }

    pub const fn new(ts: u64) -> TimeStamp {
        TimeStamp(ts)
    }

    /// Extracts physical part of a timestamp, in milliseconds.
    pub fn physical(self) -> u64 {
        self.0 >> TSO_PHYSICAL_SHIFT_BITS
    }

    pub fn next(self) -> TimeStamp {
        assert!(self.0 < u64::MAX);
        TimeStamp(self.0 + 1)
    }

    pub fn prev(self) -> TimeStamp {
        assert!(self.0 > 0);
        TimeStamp(self.0 - 1)
    }

    pub fn incr(&mut self) -> &mut TimeStamp {
        assert!(self.0 < u64::MAX);
        self.0 += 1;
        self
    }

    pub fn decr(&mut self) -> &mut TimeStamp {
        assert!(self.0 > 0);
        self.0 -= 1;
        self
    }

    pub fn is_zero(self) -> bool {
        self.0 == 0
    }

    pub fn is_max(self) -> bool {
        self.0 == u64::MAX
    }

    pub fn into_inner(self) -> u64 {
        self.0
    }
}

impl From<u64> for TimeStamp {
    fn from(ts: u64) -> TimeStamp {
        TimeStamp(ts)
    }
}

impl From<&u64> for TimeStamp {
    fn from(ts: &u64) -> TimeStamp {
        TimeStamp(*ts)
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl slog::Value for TimeStamp {
    fn serialize(
        &self,
        record: &slog::Record,
        key: slog::Key,
        serializer: &mut dyn slog::Serializer,
    ) -> slog::Result {
        slog::Value::serialize(&self.0, record, key, serializer)
    }
}

const TS_SET_USE_VEC_LIMIT: usize = 8;

/// A hybrid immutable set for timestamps.
#[derive(Debug, Clone, PartialEq)]
pub enum TsSet {
    /// When the set is empty, avoid the useless cloning of Arc.
    Empty,
    /// `Vec` is suitable when the set is small or the set is barely used, and it doesn't worth
    /// converting a `Vec` into a `HashSet`.
    Vec(Arc<[TimeStamp]>),
    /// `Set` is suitable when there are many timestamps **and** it will be queried multiple times.
    Set(Arc<HashSet<TimeStamp>>),
}

impl Default for TsSet {
    #[inline]
    fn default() -> TsSet {
        TsSet::Empty
    }
}

impl TsSet {
    /// Create a `TsSet` from the given vec of timestamps. It will select the proper internal
    /// collection type according to the size.
    #[inline]
    pub fn new(ts: Vec<TimeStamp>) -> Self {
        if ts.is_empty() {
            TsSet::Empty
        } else if ts.len() <= TS_SET_USE_VEC_LIMIT {
            // If there are too few elements in `ts`, use Vec directly instead of making a Set.
            TsSet::Vec(ts.into())
        } else {
            TsSet::Set(Arc::new(ts.into_iter().collect()))
        }
    }

    pub fn from_u64s(ts: Vec<u64>) -> Self {
        // This conversion is safe because TimeStamp is a transparent wrapper over u64.
        let ts = unsafe { ::std::mem::transmute::<Vec<u64>, Vec<TimeStamp>>(ts) };
        Self::new(ts)
    }

    pub fn vec_from_u64s(ts: Vec<u64>) -> Self {
        // This conversion is safe because TimeStamp is a transparent wrapper over u64.
        let ts = unsafe { ::std::mem::transmute::<Vec<u64>, Vec<TimeStamp>>(ts) };
        Self::vec(ts)
    }

    /// Create a `TsSet` from the given vec of timestamps, but it will be forced to use `Vec` as the
    /// internal collection type. When it's sure that the set will be queried at most once, use this
    /// is better than `TsSet::new`, since both the querying on `Vec` and the conversion from `Vec`
    /// to `HashSet` is O(N).
    #[inline]
    pub fn vec(ts: Vec<TimeStamp>) -> Self {
        if ts.is_empty() {
            TsSet::Empty
        } else {
            TsSet::Vec(ts.into())
        }
    }

    /// Query whether the given timestamp is contained in the set.
    #[inline]
    pub fn contains(&self, ts: TimeStamp) -> bool {
        match self {
            TsSet::Empty => false,
            TsSet::Vec(vec) => vec.contains(&ts),
            TsSet::Set(set) => set.contains(&ts),
        }
    }
}

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

    #[test]
    fn test_ts() {
        let physical = 1568700549751;
        let logical = 108;
        let ts = TimeStamp::compose(physical, logical);
        assert_eq!(ts, 411225436913926252.into());

        let extracted_physical = ts.physical();
        assert_eq!(extracted_physical, physical);
    }

    #[test]
    fn test_split_ts() {
        let k = b"k";
        let ts = TimeStamp(123);
        assert!(Key::split_on_ts_for(k).is_err());
        let enc = Key::from_encoded_slice(k).append_ts(ts);
        let res = Key::split_on_ts_for(enc.as_encoded()).unwrap();
        assert_eq!(res, (k.as_ref(), ts));
    }

    #[test]
    fn test_ts_set() {
        let s = TsSet::new(vec![]);
        assert_eq!(s, TsSet::Empty);
        assert!(!s.contains(1.into()));

        let s = TsSet::vec(vec![]);
        assert_eq!(s, TsSet::Empty);

        let s = TsSet::from_u64s(vec![1, 2]);
        assert_eq!(s, TsSet::Vec(vec![1.into(), 2.into()].into()));
        assert!(s.contains(1.into()));
        assert!(s.contains(2.into()));
        assert!(!s.contains(3.into()));

        let s2 = TsSet::vec(vec![1.into(), 2.into()]);
        assert_eq!(s2, s);

        let big_ts_list: Vec<TimeStamp> =
            (0..=TS_SET_USE_VEC_LIMIT as u64).map(Into::into).collect();
        let s = TsSet::new(big_ts_list.clone());
        assert_eq!(
            s,
            TsSet::Set(Arc::new(big_ts_list.clone().into_iter().collect()))
        );
        assert!(s.contains(1.into()));
        assert!(s.contains((TS_SET_USE_VEC_LIMIT as u64).into()));
        assert!(!s.contains((TS_SET_USE_VEC_LIMIT as u64 + 1).into()));

        let s = TsSet::vec(big_ts_list.clone());
        assert_eq!(s, TsSet::Vec(big_ts_list.into()));
    }
}
<|endoftext|>


<commit_before>//! [PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-state-eventtype)

use ruma_api::ruma_api;
use ruma_events::EventType;
use ruma_identifiers::{EventId, RoomId};
use serde_json::value::RawValue as RawJsonValue;

ruma_api! {
    metadata: {
        description: "Send a state event to a room associated with the empty state key.",
        method: PUT,
        name: "create_state_event_for_empty_key",
        path: "/_matrix/client/r0/rooms/:room_id/state/:event_type",
        rate_limited: false,
        requires_authentication: true,
    }

    request: {
        /// The room to set the state in.
        #[ruma_api(path)]
        pub room_id: &'a RoomId,

        /// The type of event to send.
        #[ruma_api(path)]
        pub event_type: EventType,

        /// The event's content. The type for this field will be updated in a
        /// future release, until then you can create a value using
        /// `serde_json::value::to_raw_value`.
        #[ruma_api(body)]
        pub data: Box<RawJsonValue>,
    }

    response: {
        /// A unique identifier for the event.
        pub event_id: EventId,
    }

    error: crate::Error
}
<commit_msg>Use AnyStateEventContent in send_state_event_for_empty_key<commit_after>//! [PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-state-eventtype)

use std::convert::TryFrom;

use ruma_api::{
    error::{
        FromHttpRequestError, FromHttpResponseError, IntoHttpError, RequestDeserializationError,
        ResponseDeserializationError, ServerError,
    },
    EndpointError, Metadata, Outgoing,
};
use ruma_events::{AnyStateEventContent, EventContent as _};
use ruma_identifiers::{EventId, RoomId};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue as RawJsonValue;

/// Data for a request to the `send_state_event_for_empty_key` API endpoint.
///
/// Send a state event to a room associated with the empty state key.
#[derive(Clone, Debug, Outgoing)]
#[non_exhaustive]
#[incoming_no_deserialize]
pub struct Request<'a> {
    /// The room to set the state in.
    pub room_id: &'a RoomId,

    /// The event content to send.
    pub content: &'a AnyStateEventContent,
}

impl<'a> Request<'a> {
    /// Creates a new `Request` with the given room id and event content.
    pub fn new(room_id: &'a RoomId, content: &'a AnyStateEventContent) -> Self {
        Self { room_id, content }
    }
}

/// Data in the response from the `send_state_event_for_empty_key` API endpoint.
#[derive(Clone, Debug, Outgoing)]
#[non_exhaustive]
#[incoming_no_deserialize]
pub struct Response {
    /// A unique identifier for the event.
    pub event_id: EventId,
}

impl Response {
    /// Creates a new `Response` with the given event id.
    pub fn new(event_id: EventId) -> Self {
        Self { event_id }
    }
}

const METADATA: Metadata = Metadata {
    description: "Send a state event to a room associated with the empty state key.",
    method: http::Method::PUT,
    name: "send_state_event_for_empty_key",
    path: "/_matrix/client/r0/rooms/:room_id/state/:event_type",
    rate_limited: false,
    requires_authentication: true,
};

impl TryFrom<http::Request<Vec<u8>>> for IncomingRequest {
    type Error = FromHttpRequestError;

    fn try_from(request: http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
        let path_segments: Vec<&str> = request.uri().path()[1..].split('/').collect();

        let room_id = {
            let decoded =
                match percent_encoding::percent_decode(path_segments[4].as_bytes()).decode_utf8() {
                    Ok(val) => val,
                    Err(err) => return Err(RequestDeserializationError::new(err, request).into()),
                };

            match RoomId::try_from(&*decoded) {
                Ok(val) => val,
                Err(err) => return Err(RequestDeserializationError::new(err, request).into()),
            }
        };

        let content = {
            let request_body: Box<RawJsonValue> =
                match serde_json::from_slice(request.body().as_slice()) {
                    Ok(val) => val,
                    Err(err) => return Err(RequestDeserializationError::new(err, request).into()),
                };

            let event_type = {
                match percent_encoding::percent_decode(path_segments[6].as_bytes()).decode_utf8() {
                    Ok(val) => val,
                    Err(err) => return Err(RequestDeserializationError::new(err, request).into()),
                }
            };

            match AnyStateEventContent::from_parts(&event_type, request_body) {
                Ok(content) => content,
                Err(err) => return Err(RequestDeserializationError::new(err, request).into()),
            }
        };

        Ok(Self { room_id, content })
    }
}

/// Data in the response body.
#[derive(Debug, Deserialize, Serialize)]
struct ResponseBody {
    /// A unique identifier for the event.
    event_id: EventId,
}

impl TryFrom<Response> for http::Response<Vec<u8>> {
    type Error = IntoHttpError;

    fn try_from(response: Response) -> Result<Self, Self::Error> {
        let response = http::Response::builder()
            .header(http::header::CONTENT_TYPE, "application/json")
            .body(serde_json::to_vec(&ResponseBody { event_id: response.event_id })?)
            .unwrap();

        Ok(response)
    }
}

impl TryFrom<http::Response<Vec<u8>>> for Response {
    type Error = FromHttpResponseError<crate::Error>;

    fn try_from(response: http::Response<Vec<u8>>) -> Result<Self, Self::Error> {
        if response.status().as_u16() < 400 {
            let response_body: ResponseBody =
                match serde_json::from_slice(response.body().as_slice()) {
                    Ok(val) => val,
                    Err(err) => return Err(ResponseDeserializationError::new(err, response).into()),
                };

            Ok(Self { event_id: response_body.event_id })
        } else {
            match <crate::Error as EndpointError>::try_from_response(response) {
                Ok(err) => Err(ServerError::Known(err).into()),
                Err(response_err) => Err(ServerError::Unknown(response_err).into()),
            }
        }
    }
}

impl<'a> ruma_api::OutgoingRequest for Request<'a> {
    type EndpointError = crate::Error;
    type IncomingResponse = Response;

    /// Metadata for the `send_message_event` endpoint.
    const METADATA: Metadata = METADATA;

    fn try_into_http_request(
        self,
        base_url: &str,
        access_token: Option<&str>,
    ) -> Result<http::Request<Vec<u8>>, IntoHttpError> {
        use http::header::{HeaderValue, AUTHORIZATION};
        use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};

        let http_request = http::Request::builder()
            .method(http::Method::PUT)
            .uri(format!(
                "{}/_matrix/client/r0/rooms/{}/state/{}",
                base_url.strip_suffix("/").unwrap_or(base_url),
                utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC),
                utf8_percent_encode(self.content.event_type(), NON_ALPHANUMERIC),
            ))
            .header(
                AUTHORIZATION,
                HeaderValue::from_str(&format!(
                    "Bearer {}",
                    access_token.ok_or(IntoHttpError::NeedsAuthentication)?
                ))?,
            )
            .body(serde_json::to_vec(&self.content)?)?;

        Ok(http_request)
    }
}

impl ruma_api::IncomingRequest for IncomingRequest {
    type EndpointError = crate::Error;
    type OutgoingResponse = Response;

    /// Metadata for the `send_message_event` endpoint.
    const METADATA: Metadata = METADATA;
}
<|endoftext|>


<commit_before>use model::*;
use serde::de::{Deserialize, Error as DeError, MapAccess, Visitor};
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
use std::str::FromStr;
use internal::prelude::*;

#[cfg(all(feature = "cache", feature = "model"))]
use CACHE;
#[cfg(feature = "model")]
use http;

/// An emoji reaction to a message.
#[derive(Clone, Debug, Deserialize)]
pub struct Reaction {
    /// The [`Channel`] of the associated [`Message`].
    ///
    /// [`Channel`]: enum.Channel.html
    /// [`Message`]: struct.Message.html
    pub channel_id: ChannelId,
    /// The reactive emoji used.
    pub emoji: ReactionType,
    /// The Id of the [`Message`] that was reacted to.
    ///
    /// [`Message`]: struct.Message.html
    pub message_id: MessageId,
    /// The Id of the [`User`] that sent the reaction.
    ///
    /// [`User`]: struct.User.html
    pub user_id: UserId,
}

#[cfg(feature = "model")]
impl Reaction {
    /// Deletes the reaction, but only if the current user is the user who made
    /// the reaction or has permission to.
    ///
    /// Requires the [Manage Messages] permission, _if_ the current
    /// user did not perform the reaction.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, then returns a
    /// [`ModelError::InvalidPermissions`] if the current user does not have
    /// the required [permissions].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
    /// [permissions]: permissions
    pub fn delete(&self) -> Result<()> {
        let user_id = feature_cache! {
            {
                let user = if self.user_id == CACHE.read().user.id {
                    None
                } else {
                    Some(self.user_id.0)
                };

                // If the reaction is one _not_ made by the current user, then ensure
                // that the current user has permission* to delete the reaction.
                //
                // Normally, users can only delete their own reactions.
                //
                // * The `Manage Messages` permission.
                if user.is_some() {
                    let req = Permissions::MANAGE_MESSAGES;

                    if !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
                        return Err(Error::Model(ModelError::InvalidPermissions(req)));
                    }
                }

                user
            } else {
                Some(self.user_id.0)
            }
        };

        http::delete_reaction(self.channel_id.0, self.message_id.0, user_id, &self.emoji)
    }

    /// Retrieves the [`Message`] associated with this reaction.
    ///
    /// Requires the [Read Message History] permission.
    ///
    /// **Note**: This will send a request to the REST API. Prefer maintaining
    /// your own message cache or otherwise having the message available if
    /// possible.
    ///
    /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
    /// [`Message`]: struct.Message.html
    pub fn message(&self) -> Result<Message> {
        self.channel_id.message(self.message_id)
    }

    /// Retrieves the list of [`User`]s who have reacted to a [`Message`] with a
    /// certain [`Emoji`].
    ///
    /// The default `limit` is `50` - specify otherwise to receive a different
    /// maximum number of users. The maximum that may be retrieve at a time is
    /// `100`, if a greater number is provided then it is automatically reduced.
    ///
    /// The optional `after` attribute is to retrieve the users after a certain
    /// user. This is useful for pagination.
    ///
    /// Requires the [Read Message History] permission.
    ///
    /// **Note**: This will send a request to the REST API.
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::InvalidPermissions`] if the current user does
    /// not have the required [permissions].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`Emoji`]: struct.Emoji.html
    /// [`Message`]: struct.Message.html
    /// [`User`]: struct.User.html
    /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
    /// [permissions]: permissions
    pub fn users<R, U>(&self,
                       reaction_type: R,
                       limit: Option<u8>,
                       after: Option<U>)
                       -> Result<Vec<User>>
        where R: Into<ReactionType>, U: Into<UserId> {
        http::get_reaction_users(
            self.channel_id.0,
            self.message_id.0,
            &reaction_type.into(),
            limit.unwrap_or(50),
            after.map(|u| u.into().0),
        )
    }
}

/// The type of a [`Reaction`] sent.
///
/// [`Reaction`]: struct.Reaction.html
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ReactionType {
    /// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
    /// guild.
    ///
    /// [`Emoji`]: struct.Emoji.html
    /// [`Guild`]: struct.Guild.html
    Custom {
        /// The Id of the custom [`Emoji`].
        ///
        /// [`Emoji`]: struct.Emoji.html
        id: EmojiId,
        /// The name of the custom emoji. This is primarily used for decoration
        /// and distinguishing the emoji client-side.
        name: Option<String>,
    },
    /// A reaction with a twemoji.
    Unicode(String),
}

impl<'de> Deserialize<'de> for ReactionType {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "snake_case")]
        enum Field {
            Id,
            Name,
        }

        struct ReactionTypeVisitor;

        impl<'de> Visitor<'de> for ReactionTypeVisitor {
            type Value = ReactionType;

            fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
                formatter.write_str("enum ReactionType")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> StdResult<Self::Value, V::Error> {
                let mut id = None;
                let mut name = None;

                while let Some(key) = map.next_key()? {
                    match key {
                        Field::Id => {
                            if id.is_some() {
                                return Err(DeError::duplicate_field("id"));
                            }

                            if let Ok(emoji_id) = map.next_value::<EmojiId>() {
                                id = Some(emoji_id)
                            }
                        },
                        Field::Name => {
                            if name.is_some() {
                                return Err(DeError::duplicate_field("name"));
                            }

                            name = Some(map.next_value()?);
                        },
                    }
                }

                let name = name.ok_or_else(|| DeError::missing_field("name"))?;

                Ok(if let Some(id) = id {
                    ReactionType::Custom {
                        id: id,
                        name: name,
                    }
                } else {
                    ReactionType::Unicode(name.unwrap())
                })
            }
        }

        deserializer.deserialize_map(ReactionTypeVisitor)
    }
}

#[cfg(any(feature = "model", feature = "http"))]
impl ReactionType {
    /// Creates a data-esque display of the type. This is not very useful for
    /// displaying, as the primary client can not render it, but can be useful
    /// for debugging.
    ///
    /// **Note**: This is mainly for use internally. There is otherwise most
    /// likely little use for it.
    pub fn as_data(&self) -> String {
        match *self {
            ReactionType::Custom {
                id,
                ref name,
            } => format!("{}:{}", name.as_ref().map_or("", |s| s.as_str()), id),
            ReactionType::Unicode(ref unicode) => unicode.clone(),
        }
    }
}

#[cfg(feature = "model")]
impl From<char> for ReactionType {
    /// Creates a `ReactionType` from a `char`.
    ///
    /// # Examples
    ///
    /// Reacting to a message with an apple:
    ///
    /// ```rust,no_run
    /// # use serenity::model::ChannelId;
    /// # use std::error::Error;
    /// #
    /// # fn try_main() -> Result<(), Box<Error>> {
    /// #     let message = ChannelId(0).message(0)?;
    /// #
    /// message.react('🍎')?;
    /// #     Ok(())
    /// # }
    /// #
    /// # fn main() {
    /// #     try_main().unwrap();
    /// # }
    /// ```
    fn from(ch: char) -> ReactionType { ReactionType::Unicode(ch.to_string()) }
}

impl From<Emoji> for ReactionType {
    fn from(emoji: Emoji) -> ReactionType {
        ReactionType::Custom {
            id: emoji.id,
            name: Some(emoji.name),
        }
    }
}

impl From<EmojiId> for ReactionType {
    fn from(emoji_id: EmojiId) -> ReactionType {
        ReactionType::Custom {
            id: emoji_id,
            name: None
        }
    }
}

impl From<EmojiIdentifier> for ReactionType {
    fn from(emoji_id: EmojiIdentifier) -> ReactionType {
        ReactionType::Custom {
            id: emoji_id.id,
            name: Some(emoji_id.name)
        }
    }
}

impl From<String> for ReactionType {
    fn from(unicode: String) -> ReactionType { ReactionType::Unicode(unicode) }
}

impl<'a> From<&'a str> for ReactionType {
    /// Creates a `ReactionType` from a string slice.
    ///
    /// # Examples
    ///
    /// Creating a `ReactionType` from a `🍎`, modeling a similar API as the
    /// rest of the library:
    ///
    /// ```rust
    /// use serenity::model::ReactionType;
    ///
    /// fn foo<R: Into<ReactionType>>(bar: R) {
    ///     println!("{:?}", bar.into());
    /// }
    ///
    /// foo("🍎");
    /// ```
    fn from(unicode: &str) -> ReactionType { ReactionType::Unicode(unicode.to_string()) }
}

// TODO: Change this to `!` once it becomes stable.

#[derive(Debug)]
pub enum NeverFails {}

impl Display for NeverFails {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "never fails")
    }
}

impl StdError for NeverFails {
    fn description(&self) -> &str {
        "never fails"
    }
}

impl FromStr for ReactionType {
    type Err = NeverFails;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        Ok(ReactionType::from(s))
    }
}

impl Display for ReactionType {
    /// Formats the reaction type, displaying the associated emoji in a
    /// way that clients can understand.
    ///
    /// If the type is a [custom][`ReactionType::Custom`] emoji, then refer to
    /// the documentation for [emoji's formatter][`Emoji::fmt`] on how this is
    /// displayed. Otherwise, if the type is a
    /// [unicode][`ReactionType::Unicode`], then the inner unicode is displayed.
    ///
    /// [`Emoji::fmt`]: struct.Emoji.html#method.fmt
    /// [`ReactionType::Custom`]: enum.ReactionType.html#variant.Custom
    /// [`ReactionType::Unicode`]: enum.ReactionType.html#variant.Unicode
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            ReactionType::Custom {
                id,
                ref name,
            } => {
                f.write_char('<')?;
                f.write_char(':')?;
                f.write_str(name.as_ref().map_or("", |s| s.as_str()))?;
                f.write_char(':')?;
                Display::fmt(&id, f)?;
                f.write_char('>')
            },
            ReactionType::Unicode(ref unicode) => f.write_str(unicode),
        }
    }
}
<commit_msg>Add `model::Reaction::channel`<commit_after>use model::*;
use serde::de::{Deserialize, Error as DeError, MapAccess, Visitor};
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
use std::str::FromStr;
use internal::prelude::*;

#[cfg(all(feature = "cache", feature = "model"))]
use CACHE;
#[cfg(feature = "model")]
use http;

/// An emoji reaction to a message.
#[derive(Clone, Debug, Deserialize)]
pub struct Reaction {
    /// The [`Channel`] of the associated [`Message`].
    ///
    /// [`Channel`]: enum.Channel.html
    /// [`Message`]: struct.Message.html
    pub channel_id: ChannelId,
    /// The reactive emoji used.
    pub emoji: ReactionType,
    /// The Id of the [`Message`] that was reacted to.
    ///
    /// [`Message`]: struct.Message.html
    pub message_id: MessageId,
    /// The Id of the [`User`] that sent the reaction.
    ///
    /// [`User`]: struct.User.html
    pub user_id: UserId,
}

#[cfg(feature = "model")]
impl Reaction {
    /// Retrieves the associated the reaction was made in.
    ///
    /// If the cache is enabled, this will search for the alread-cached channel.
    /// If not - or the channel was not found - this will perform a request over
    /// the REST API for the channel.
    ///
    /// Requires the [Read Message History] permission.
    ///
    /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
    #[inline]
    pub fn channel(&self) -> Result<Channel> {
        self.channel_id.get()
    }

    /// Deletes the reaction, but only if the current user is the user who made
    /// the reaction or has permission to.
    ///
    /// Requires the [Manage Messages] permission, _if_ the current
    /// user did not perform the reaction.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, then returns a
    /// [`ModelError::InvalidPermissions`] if the current user does not have
    /// the required [permissions].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
    /// [permissions]: permissions
    pub fn delete(&self) -> Result<()> {
        let user_id = feature_cache! {
            {
                let user = if self.user_id == CACHE.read().user.id {
                    None
                } else {
                    Some(self.user_id.0)
                };

                // If the reaction is one _not_ made by the current user, then ensure
                // that the current user has permission* to delete the reaction.
                //
                // Normally, users can only delete their own reactions.
                //
                // * The `Manage Messages` permission.
                if user.is_some() {
                    let req = Permissions::MANAGE_MESSAGES;

                    if !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
                        return Err(Error::Model(ModelError::InvalidPermissions(req)));
                    }
                }

                user
            } else {
                Some(self.user_id.0)
            }
        };

        http::delete_reaction(self.channel_id.0, self.message_id.0, user_id, &self.emoji)
    }

    /// Retrieves the [`Message`] associated with this reaction.
    ///
    /// Requires the [Read Message History] permission.
    ///
    /// **Note**: This will send a request to the REST API. Prefer maintaining
    /// your own message cache or otherwise having the message available if
    /// possible.
    ///
    /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
    /// [`Message`]: struct.Message.html
    #[inline]
    pub fn message(&self) -> Result<Message> {
        self.channel_id.message(self.message_id)
    }

    /// Retrieves the list of [`User`]s who have reacted to a [`Message`] with a
    /// certain [`Emoji`].
    ///
    /// The default `limit` is `50` - specify otherwise to receive a different
    /// maximum number of users. The maximum that may be retrieve at a time is
    /// `100`, if a greater number is provided then it is automatically reduced.
    ///
    /// The optional `after` attribute is to retrieve the users after a certain
    /// user. This is useful for pagination.
    ///
    /// Requires the [Read Message History] permission.
    ///
    /// **Note**: This will send a request to the REST API.
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::InvalidPermissions`] if the current user does
    /// not have the required [permissions].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`Emoji`]: struct.Emoji.html
    /// [`Message`]: struct.Message.html
    /// [`User`]: struct.User.html
    /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
    /// [permissions]: permissions
    pub fn users<R, U>(&self,
                       reaction_type: R,
                       limit: Option<u8>,
                       after: Option<U>)
                       -> Result<Vec<User>>
        where R: Into<ReactionType>, U: Into<UserId> {
        http::get_reaction_users(
            self.channel_id.0,
            self.message_id.0,
            &reaction_type.into(),
            limit.unwrap_or(50),
            after.map(|u| u.into().0),
        )
    }
}

/// The type of a [`Reaction`] sent.
///
/// [`Reaction`]: struct.Reaction.html
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ReactionType {
    /// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
    /// guild.
    ///
    /// [`Emoji`]: struct.Emoji.html
    /// [`Guild`]: struct.Guild.html
    Custom {
        /// The Id of the custom [`Emoji`].
        ///
        /// [`Emoji`]: struct.Emoji.html
        id: EmojiId,
        /// The name of the custom emoji. This is primarily used for decoration
        /// and distinguishing the emoji client-side.
        name: Option<String>,
    },
    /// A reaction with a twemoji.
    Unicode(String),
}

impl<'de> Deserialize<'de> for ReactionType {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(field_identifier, rename_all = "snake_case")]
        enum Field {
            Id,
            Name,
        }

        struct ReactionTypeVisitor;

        impl<'de> Visitor<'de> for ReactionTypeVisitor {
            type Value = ReactionType;

            fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
                formatter.write_str("enum ReactionType")
            }

            fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> StdResult<Self::Value, V::Error> {
                let mut id = None;
                let mut name = None;

                while let Some(key) = map.next_key()? {
                    match key {
                        Field::Id => {
                            if id.is_some() {
                                return Err(DeError::duplicate_field("id"));
                            }

                            if let Ok(emoji_id) = map.next_value::<EmojiId>() {
                                id = Some(emoji_id)
                            }
                        },
                        Field::Name => {
                            if name.is_some() {
                                return Err(DeError::duplicate_field("name"));
                            }

                            name = Some(map.next_value()?);
                        },
                    }
                }

                let name = name.ok_or_else(|| DeError::missing_field("name"))?;

                Ok(if let Some(id) = id {
                    ReactionType::Custom {
                        id: id,
                        name: name,
                    }
                } else {
                    ReactionType::Unicode(name.unwrap())
                })
            }
        }

        deserializer.deserialize_map(ReactionTypeVisitor)
    }
}

#[cfg(any(feature = "model", feature = "http"))]
impl ReactionType {
    /// Creates a data-esque display of the type. This is not very useful for
    /// displaying, as the primary client can not render it, but can be useful
    /// for debugging.
    ///
    /// **Note**: This is mainly for use internally. There is otherwise most
    /// likely little use for it.
    pub fn as_data(&self) -> String {
        match *self {
            ReactionType::Custom {
                id,
                ref name,
            } => format!("{}:{}", name.as_ref().map_or("", |s| s.as_str()), id),
            ReactionType::Unicode(ref unicode) => unicode.clone(),
        }
    }
}

#[cfg(feature = "model")]
impl From<char> for ReactionType {
    /// Creates a `ReactionType` from a `char`.
    ///
    /// # Examples
    ///
    /// Reacting to a message with an apple:
    ///
    /// ```rust,no_run
    /// # use serenity::model::ChannelId;
    /// # use std::error::Error;
    /// #
    /// # fn try_main() -> Result<(), Box<Error>> {
    /// #     let message = ChannelId(0).message(0)?;
    /// #
    /// message.react('🍎')?;
    /// #     Ok(())
    /// # }
    /// #
    /// # fn main() {
    /// #     try_main().unwrap();
    /// # }
    /// ```
    fn from(ch: char) -> ReactionType { ReactionType::Unicode(ch.to_string()) }
}

impl From<Emoji> for ReactionType {
    fn from(emoji: Emoji) -> ReactionType {
        ReactionType::Custom {
            id: emoji.id,
            name: Some(emoji.name),
        }
    }
}

impl From<EmojiId> for ReactionType {
    fn from(emoji_id: EmojiId) -> ReactionType {
        ReactionType::Custom {
            id: emoji_id,
            name: None
        }
    }
}

impl From<EmojiIdentifier> for ReactionType {
    fn from(emoji_id: EmojiIdentifier) -> ReactionType {
        ReactionType::Custom {
            id: emoji_id.id,
            name: Some(emoji_id.name)
        }
    }
}

impl From<String> for ReactionType {
    fn from(unicode: String) -> ReactionType { ReactionType::Unicode(unicode) }
}

impl<'a> From<&'a str> for ReactionType {
    /// Creates a `ReactionType` from a string slice.
    ///
    /// # Examples
    ///
    /// Creating a `ReactionType` from a `🍎`, modeling a similar API as the
    /// rest of the library:
    ///
    /// ```rust
    /// use serenity::model::ReactionType;
    ///
    /// fn foo<R: Into<ReactionType>>(bar: R) {
    ///     println!("{:?}", bar.into());
    /// }
    ///
    /// foo("🍎");
    /// ```
    fn from(unicode: &str) -> ReactionType { ReactionType::Unicode(unicode.to_string()) }
}

// TODO: Change this to `!` once it becomes stable.

#[derive(Debug)]
pub enum NeverFails {}

impl Display for NeverFails {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "never fails")
    }
}

impl StdError for NeverFails {
    fn description(&self) -> &str {
        "never fails"
    }
}

impl FromStr for ReactionType {
    type Err = NeverFails;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        Ok(ReactionType::from(s))
    }
}

impl Display for ReactionType {
    /// Formats the reaction type, displaying the associated emoji in a
    /// way that clients can understand.
    ///
    /// If the type is a [custom][`ReactionType::Custom`] emoji, then refer to
    /// the documentation for [emoji's formatter][`Emoji::fmt`] on how this is
    /// displayed. Otherwise, if the type is a
    /// [unicode][`ReactionType::Unicode`], then the inner unicode is displayed.
    ///
    /// [`Emoji::fmt`]: struct.Emoji.html#method.fmt
    /// [`ReactionType::Custom`]: enum.ReactionType.html#variant.Custom
    /// [`ReactionType::Unicode`]: enum.ReactionType.html#variant.Unicode
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            ReactionType::Custom {
                id,
                ref name,
            } => {
                f.write_char('<')?;
                f.write_char(':')?;
                f.write_str(name.as_ref().map_or("", |s| s.as_str()))?;
                f.write_char(':')?;
                Display::fmt(&id, f)?;
                f.write_char('>')
            },
            ReactionType::Unicode(ref unicode) => f.write_str(unicode),
        }
    }
}
<|endoftext|>