<commit_before>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
};

use matches::matches;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::Error;

/// Room version identifiers cannot be more than 32 code points.
const MAX_CODE_POINTS: usize = 32;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(CustomRoomVersion),
}

impl RoomVersionId {
    /// Creates a version 1 room ID.
    #[deprecated = "use RoomVersionId::Version1 instead"]
    pub fn version_1() -> Self {
        Self::Version1
    }

    /// Creates a version 2 room ID.
    #[deprecated = "use RoomVersionId::Version2 instead"]
    pub fn version_2() -> Self {
        Self::Version2
    }

    /// Creates a version 3 room ID.
    #[deprecated = "use RoomVersionId::Version3 instead"]
    pub fn version_3() -> Self {
        Self::Version3
    }

    /// Creates a version 4 room ID.
    #[deprecated = "use RoomVersionId::Version4 instead"]
    pub fn version_4() -> Self {
        Self::Version4
    }

    /// Creates a version 5 room ID.
    #[deprecated = "use RoomVersionId::Version5 instead"]
    pub fn version_5() -> Self {
        Self::Version5
    }

    /// Creates a version 6 room ID.
    #[deprecated = "use RoomVersionId::Version6 instead"]
    pub fn version_6() -> Self {
        Self::Version6
    }

    /// Creates a string slice from this `RoomVersionId`
    pub fn as_str(&self) -> &str {
        match &self {
            Self::Version1 => "1",
            Self::Version2 => "2",
            Self::Version3 => "3",
            Self::Version4 => "4",
            Self::Version5 => "5",
            Self::Version6 => "6",
            Self::Custom(version) => version.as_str(),
        }
    }

    /// Whether or not this room version is an official one specified by the Matrix protocol.
    pub fn is_official(&self) -> bool {
        !self.is_custom()
    }

    /// Whether or not this is a custom room version.
    pub fn is_custom(&self) -> bool {
        matches!(self, Self::Custom(_))
    }

    /// Whether or not this is a version 1 room.
    #[deprecated = "compare to RoomVersionId::Version1 instead"]
    pub fn is_version_1(&self) -> bool {
        matches!(self, Self::Version1)
    }

    /// Whether or not this is a version 2 room.
    #[deprecated = "compare to RoomVersionId::Version2 instead"]
    pub fn is_version_2(&self) -> bool {
        matches!(self, Self::Version2)
    }

    /// Whether or not this is a version 3 room.
    #[deprecated = "compare to RoomVersionId::Version3 instead"]
    pub fn is_version_3(&self) -> bool {
        matches!(self, Self::Version3)
    }

    /// Whether or not this is a version 4 room.
    #[deprecated = "compare to RoomVersionId::Version4 instead"]
    pub fn is_version_4(&self) -> bool {
        matches!(self, Self::Version4)
    }

    /// Whether or not this is a version 5 room.
    #[deprecated = "compare to RoomVersionId::Version5 instead"]
    pub fn is_version_5(&self) -> bool {
        matches!(self, Self::Version5)
    }

    /// Whether or not this is a version 6 room.
    #[deprecated = "compare to RoomVersionId::Version6 instead"]
    pub fn is_version_6(&self) -> bool {
        matches!(self, Self::Version5)
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id {
            RoomVersionId::Version1 => "1".to_owned(),
            RoomVersionId::Version2 => "2".to_owned(),
            RoomVersionId::Version3 => "3".to_owned(),
            RoomVersionId::Version4 => "4".to_owned(),
            RoomVersionId::Version5 => "5".to_owned(),
            RoomVersionId::Version6 => "6".to_owned(),
            RoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId::Version1,
        "2" => RoomVersionId::Version2,
        "3" => RoomVersionId::Version3,
        "4" => RoomVersionId::Version4,
        "5" => RoomVersionId::Version5,
        custom => {
            if custom.is_empty() {
                return Err(Error::MinimumLengthNotSatisfied);
            } else if custom.chars().count() > MAX_CODE_POINTS {
                return Err(Error::MaximumLengthExceeded);
            } else {
                RoomVersionId::Custom(CustomRoomVersion(room_version_id.into()))
            }
        }
    };

    Ok(version)
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_str()
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomRoomVersion(Box<str>);

impl CustomRoomVersion {
    /// Creates a string slice from this `CustomRoomVersion`
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<CustomRoomVersion> for String {
    fn from(v: CustomRoomVersion) -> Self {
        v.0.into()
    }
}

impl AsRef<str> for CustomRoomVersion {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    use matches::assert_matches;
    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::RoomVersionId;
    use crate::error::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::MinimumLengthNotSatisfied));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert_matches!(deserialized, RoomVersionId::Version1);
        assert!(deserialized.is_official());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_custom());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }

    #[test]
    #[allow(deprecated)]
    fn constructors() {
        assert!(RoomVersionId::version_1().is_version_1());
        assert!(RoomVersionId::version_2().is_version_2());
        assert!(RoomVersionId::version_3().is_version_3());
        assert!(RoomVersionId::version_4().is_version_4());
        assert!(RoomVersionId::version_5().is_version_5());
    }

    #[test]
    #[allow(deprecated, clippy::cognitive_complexity)]
    fn predicate_methods() {
        let version_1 = RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.");
        let version_2 = RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.");
        let version_3 = RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.");
        let version_4 = RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.");
        let version_5 = RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.");
        let custom = RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.");

        assert!(version_1.is_version_1());
        assert!(version_2.is_version_2());
        assert!(version_3.is_version_3());
        assert!(version_4.is_version_4());
        assert!(version_5.is_version_5());

        assert!(!version_1.is_version_2());
        assert!(!version_1.is_version_3());
        assert!(!version_1.is_version_4());
        assert!(!version_1.is_version_5());

        assert!(version_1.is_official());
        assert!(version_2.is_official());
        assert!(version_3.is_official());
        assert!(version_4.is_official());
        assert!(version_5.is_official());

        assert!(!version_1.is_custom());
        assert!(!version_2.is_custom());
        assert!(!version_3.is_custom());
        assert!(!version_4.is_custom());
        assert!(!version_5.is_custom());

        assert!(custom.is_custom());
        assert!(!custom.is_official());
        assert!(!custom.is_version_1());
        assert!(!custom.is_version_2());
        assert!(!custom.is_version_3());
        assert!(!custom.is_version_4());
        assert!(!custom.is_version_5());
    }
}
<commit_msg>Fix unused import<commit_after>//! Matrix room version identifiers.

use std::{
    cmp::Ordering,
    convert::TryFrom,
    fmt::{self, Display, Formatter},
};

use matches::matches;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::Error;

/// Room version identifiers cannot be more than 32 code points.
const MAX_CODE_POINTS: usize = 32;

/// A Matrix room version ID.
///
/// A `RoomVersionId` can be or converted or deserialized from a string slice, and can be converted
/// or serialized back into a string as needed.
///
/// ```
/// # use std::convert::TryFrom;
/// # use ruma_identifiers::RoomVersionId;
/// assert_eq!(RoomVersionId::try_from("1").unwrap().as_ref(), "1");
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RoomVersionId {
    /// A version 1 room.
    Version1,

    /// A version 2 room.
    Version2,

    /// A version 3 room.
    Version3,

    /// A version 4 room.
    Version4,

    /// A version 5 room.
    Version5,

    /// A version 6 room.
    Version6,

    /// A custom room version.
    Custom(CustomRoomVersion),
}

impl RoomVersionId {
    /// Creates a version 1 room ID.
    #[deprecated = "use RoomVersionId::Version1 instead"]
    pub fn version_1() -> Self {
        Self::Version1
    }

    /// Creates a version 2 room ID.
    #[deprecated = "use RoomVersionId::Version2 instead"]
    pub fn version_2() -> Self {
        Self::Version2
    }

    /// Creates a version 3 room ID.
    #[deprecated = "use RoomVersionId::Version3 instead"]
    pub fn version_3() -> Self {
        Self::Version3
    }

    /// Creates a version 4 room ID.
    #[deprecated = "use RoomVersionId::Version4 instead"]
    pub fn version_4() -> Self {
        Self::Version4
    }

    /// Creates a version 5 room ID.
    #[deprecated = "use RoomVersionId::Version5 instead"]
    pub fn version_5() -> Self {
        Self::Version5
    }

    /// Creates a version 6 room ID.
    #[deprecated = "use RoomVersionId::Version6 instead"]
    pub fn version_6() -> Self {
        Self::Version6
    }

    /// Creates a string slice from this `RoomVersionId`
    pub fn as_str(&self) -> &str {
        match &self {
            Self::Version1 => "1",
            Self::Version2 => "2",
            Self::Version3 => "3",
            Self::Version4 => "4",
            Self::Version5 => "5",
            Self::Version6 => "6",
            Self::Custom(version) => version.as_str(),
        }
    }

    /// Whether or not this room version is an official one specified by the Matrix protocol.
    pub fn is_official(&self) -> bool {
        !self.is_custom()
    }

    /// Whether or not this is a custom room version.
    pub fn is_custom(&self) -> bool {
        matches!(self, Self::Custom(_))
    }

    /// Whether or not this is a version 1 room.
    #[deprecated = "compare to RoomVersionId::Version1 instead"]
    pub fn is_version_1(&self) -> bool {
        matches!(self, Self::Version1)
    }

    /// Whether or not this is a version 2 room.
    #[deprecated = "compare to RoomVersionId::Version2 instead"]
    pub fn is_version_2(&self) -> bool {
        matches!(self, Self::Version2)
    }

    /// Whether or not this is a version 3 room.
    #[deprecated = "compare to RoomVersionId::Version3 instead"]
    pub fn is_version_3(&self) -> bool {
        matches!(self, Self::Version3)
    }

    /// Whether or not this is a version 4 room.
    #[deprecated = "compare to RoomVersionId::Version4 instead"]
    pub fn is_version_4(&self) -> bool {
        matches!(self, Self::Version4)
    }

    /// Whether or not this is a version 5 room.
    #[deprecated = "compare to RoomVersionId::Version5 instead"]
    pub fn is_version_5(&self) -> bool {
        matches!(self, Self::Version5)
    }

    /// Whether or not this is a version 6 room.
    #[deprecated = "compare to RoomVersionId::Version6 instead"]
    pub fn is_version_6(&self) -> bool {
        matches!(self, Self::Version5)
    }
}

impl From<RoomVersionId> for String {
    fn from(id: RoomVersionId) -> Self {
        match id {
            RoomVersionId::Version1 => "1".to_owned(),
            RoomVersionId::Version2 => "2".to_owned(),
            RoomVersionId::Version3 => "3".to_owned(),
            RoomVersionId::Version4 => "4".to_owned(),
            RoomVersionId::Version5 => "5".to_owned(),
            RoomVersionId::Version6 => "6".to_owned(),
            RoomVersionId::Custom(version) => version.into(),
        }
    }
}

impl AsRef<str> for RoomVersionId {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Display for RoomVersionId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl PartialOrd for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn partial_cmp(&self, other: &RoomVersionId) -> Option<Ordering> {
        self.as_ref().partial_cmp(other.as_ref())
    }
}

impl Ord for RoomVersionId {
    /// Compare the two given room version IDs by comparing their string representations.
    ///
    /// Please be aware that room version IDs don't have a defined ordering in the Matrix
    /// specification. This implementation only exists to be able to use `RoomVersionId`s or
    /// types containing `RoomVersionId`s as `BTreeMap` keys.
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_ref().cmp(other.as_ref())
    }
}

#[cfg(feature = "serde")]
impl Serialize for RoomVersionId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RoomVersionId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        crate::deserialize_id(deserializer, "a Matrix room version ID as a string")
    }
}

/// Attempts to create a new Matrix room version ID from a string representation.
fn try_from<S>(room_version_id: S) -> Result<RoomVersionId, Error>
where
    S: AsRef<str> + Into<Box<str>>,
{
    let version = match room_version_id.as_ref() {
        "1" => RoomVersionId::Version1,
        "2" => RoomVersionId::Version2,
        "3" => RoomVersionId::Version3,
        "4" => RoomVersionId::Version4,
        "5" => RoomVersionId::Version5,
        custom => {
            if custom.is_empty() {
                return Err(Error::MinimumLengthNotSatisfied);
            } else if custom.chars().count() > MAX_CODE_POINTS {
                return Err(Error::MaximumLengthExceeded);
            } else {
                RoomVersionId::Custom(CustomRoomVersion(room_version_id.into()))
            }
        }
    };

    Ok(version)
}

impl TryFrom<&str> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: &str) -> Result<Self, Error> {
        try_from(s)
    }
}

impl TryFrom<String> for RoomVersionId {
    type Error = crate::error::Error;

    fn try_from(s: String) -> Result<Self, Error> {
        try_from(s)
    }
}

impl PartialEq<&str> for RoomVersionId {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<RoomVersionId> for &str {
    fn eq(&self, other: &RoomVersionId) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<String> for RoomVersionId {
    fn eq(&self, other: &String) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<RoomVersionId> for String {
    fn eq(&self, other: &RoomVersionId) -> bool {
        self == other.as_str()
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomRoomVersion(Box<str>);

impl CustomRoomVersion {
    /// Creates a string slice from this `CustomRoomVersion`
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<CustomRoomVersion> for String {
    fn from(v: CustomRoomVersion) -> Self {
        v.0.into()
    }
}

impl AsRef<str> for CustomRoomVersion {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[cfg(feature = "serde")]
    use serde_json::{from_str, to_string};

    use super::RoomVersionId;
    use crate::error::Error;

    #[test]
    fn valid_version_1_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.").as_ref(),
            "1"
        );
    }

    #[test]
    fn valid_version_2_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.").as_ref(),
            "2"
        );
    }

    #[test]
    fn valid_version_3_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.").as_ref(),
            "3"
        );
    }

    #[test]
    fn valid_version_4_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.").as_ref(),
            "4"
        );
    }

    #[test]
    fn valid_version_5_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.").as_ref(),
            "5"
        );
    }

    #[test]
    fn valid_custom_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.").as_ref(),
            "io.ruma.1"
        );
    }

    #[test]
    fn empty_room_version_id() {
        assert_eq!(RoomVersionId::try_from(""), Err(Error::MinimumLengthNotSatisfied));
    }

    #[test]
    fn over_max_code_point_room_version_id() {
        assert_eq!(
            RoomVersionId::try_from("0123456789012345678901234567890123456789"),
            Err(Error::MaximumLengthExceeded)
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_official_room_id() {
        assert_eq!(
            to_string(&RoomVersionId::try_from("1").expect("Failed to create RoomVersionId."))
                .expect("Failed to convert RoomVersionId to JSON."),
            r#""1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_official_room_id() {
        use matches::assert_matches;

        let deserialized =
            from_str::<RoomVersionId>(r#""1""#).expect("Failed to convert RoomVersionId to JSON.");

        assert_matches!(deserialized, RoomVersionId::Version1);
        assert!(deserialized.is_official());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.")
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn serialize_custom_room_id() {
        assert_eq!(
            to_string(
                &RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
            )
            .expect("Failed to convert RoomVersionId to JSON."),
            r#""io.ruma.1""#
        );
    }

    #[cfg(feature = "serde")]
    #[test]
    fn deserialize_custom_room_id() {
        let deserialized = from_str::<RoomVersionId>(r#""io.ruma.1""#)
            .expect("Failed to convert RoomVersionId to JSON.");

        assert!(deserialized.is_custom());

        assert_eq!(
            deserialized,
            RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.")
        );
    }

    #[test]
    #[allow(deprecated)]
    fn constructors() {
        assert!(RoomVersionId::version_1().is_version_1());
        assert!(RoomVersionId::version_2().is_version_2());
        assert!(RoomVersionId::version_3().is_version_3());
        assert!(RoomVersionId::version_4().is_version_4());
        assert!(RoomVersionId::version_5().is_version_5());
    }

    #[test]
    #[allow(deprecated, clippy::cognitive_complexity)]
    fn predicate_methods() {
        let version_1 = RoomVersionId::try_from("1").expect("Failed to create RoomVersionId.");
        let version_2 = RoomVersionId::try_from("2").expect("Failed to create RoomVersionId.");
        let version_3 = RoomVersionId::try_from("3").expect("Failed to create RoomVersionId.");
        let version_4 = RoomVersionId::try_from("4").expect("Failed to create RoomVersionId.");
        let version_5 = RoomVersionId::try_from("5").expect("Failed to create RoomVersionId.");
        let custom = RoomVersionId::try_from("io.ruma.1").expect("Failed to create RoomVersionId.");

        assert!(version_1.is_version_1());
        assert!(version_2.is_version_2());
        assert!(version_3.is_version_3());
        assert!(version_4.is_version_4());
        assert!(version_5.is_version_5());

        assert!(!version_1.is_version_2());
        assert!(!version_1.is_version_3());
        assert!(!version_1.is_version_4());
        assert!(!version_1.is_version_5());

        assert!(version_1.is_official());
        assert!(version_2.is_official());
        assert!(version_3.is_official());
        assert!(version_4.is_official());
        assert!(version_5.is_official());

        assert!(!version_1.is_custom());
        assert!(!version_2.is_custom());
        assert!(!version_3.is_custom());
        assert!(!version_4.is_custom());
        assert!(!version_5.is_custom());

        assert!(custom.is_custom());
        assert!(!custom.is_official());
        assert!(!custom.is_version_1());
        assert!(!custom.is_version_2());
        assert!(!custom.is_version_3());
        assert!(!custom.is_version_4());
        assert!(!custom.is_version_5());
    }
}
<|endoftext|>


<commit_before>use common::consts::get_version_string;
use platform::process::get_pid;
use platform::time::time_now;
use storage::Cache;
use storage::CacheError;
use storage::Key;
use storage::Value;
use tcp_transport::stats::TransportStats;

use super::cmd::Cmd;
use super::cmd::Delete;
use super::cmd::FlushAll;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::SetInstr;
use super::cmd::Stat;
use super::cmd::Touch;
use super::cmd::Value as CmdValue;
use super::util::convert_exptime;
use super::util::from_cache_err;

// For use to get an early exit from a function. The first parameter is a bool
// to indicate whether to omit responses (returns Resp::Empty instead). The
// second parameter is an expression that evaluates to Option<Resp>. In case
// of None no return is made ie. execution continues. In case of Some(resp) we
// perform return resp;
//
// maybe_reply_stmt!(!noreply, match rv {
//     Ok(_) => None,   // do not return
//     Err(_) => Some(Resp::Error),
// });
//
// ->
//
// let opt = expr_provided;
// if !opt.is_none() {
//     if !noreply {
//         return opt.unwrap();
//     } else {
//         return Resp::Empty;
//     }
// }
macro_rules! maybe_reply_stmt {
    ( $cond:expr, $opt:expr ) => {
        {
            if !$opt.is_none() {
                if $cond {
                    return $opt.unwrap();
                } else {
                    return Resp::Empty;
                }
            };
        }
    };
}

// For use to conditionally produce a return value. The first parameter is used
// to indicate whether to omit responses (returns Resp::Empty instead). The
// second parameter is an expression that evaluates to Resp.
//
// maybe_reply_expr!(!noreply, match rv {
//     Ok(_) => Resp::Stored,
//     Err(_) => Resp::Error,
// });
//
// ->
//
// let resp = expr_provided;
// if !noreply {
//     resp
// } else {
//     Resp::Empty
// }
macro_rules! maybe_reply_expr {
    ( $cond:expr, $resp:expr ) => {
        {
            if $cond {
                $resp
            } else {
                Resp::Empty
            }
        }
    };
}

struct DriverStats {
    cmd_get: u64,
    cmd_set: u64,
    cmd_flush: u64,
    cmd_touch: u64,
}

impl DriverStats {
    pub fn new() -> DriverStats {
        DriverStats {
            cmd_get: 0,
            cmd_set: 0,
            cmd_flush: 0,
            cmd_touch: 0,
        }
    }
}

pub struct Driver {
    cache: Cache,
    time_start: f64,

    stats: DriverStats,
    transport_stats: TransportStats, // this is a global snapshot
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver {
            cache: cache,
            stats: DriverStats::new(),
            time_start: time_now(),
            transport_stats: TransportStats::new(),
        }
    }

    fn set_exptime(&self, value: &mut Value, exptime: u32) {
        // If exptime is greater than zero we need to set it on the value
        if exptime > 0 {
            let tm;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if exptime > 60 * 60 * 24 * 30 {
                tm = exptime as f64;

            } else {
                // Otherwise it's relative from now
                tm = time_now() + exptime as f64;
            }

            value.set_exptime(tm);
        }
    }

    fn do_add(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Do we store this item already? If so it's an early exit.
        let rv = self.cache.contains_key(&key);
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(true) => Some(Resp::NotStored),
                              Ok(false) => None,
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_append(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Load the value
        let rv = self.cache.remove(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Err(CacheError::KeyNotFound) => {
                                  Some(Resp::NotStored)
                              }
                              Err(ref err) => Some(from_cache_err(err)),
                              _ => None,
                          });

        // Update the value
        let mut value = rv.unwrap();
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        // Append the data we just received to the blob that is there
        value.item.extend(set.data);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_delete(&mut self, delete: Delete) -> Resp {
        let key = Key::new(delete.key.clone().into_bytes());

        let rv = self.cache.remove(&key);

        maybe_reply_expr!(!delete.noreply,
                          match rv {
                              Ok(_) => Resp::Deleted,
                              Err(CacheError::KeyNotFound) => Resp::NotFound,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_flush_all(&mut self, flush_all: FlushAll) -> Resp {
        let exptime: f64 = match flush_all.exptime {
            Some(exptime) => convert_exptime(flush_all.exptime.unwrap()).unwrap(),
            None => time_now(),
        };

        let rv = self.cache.flush_all(exptime);

        maybe_reply_expr!(!flush_all.noreply,
                          match rv {
                              Ok(_) => Resp::Ok,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // Update stats
        self.stats.cmd_get += 1;

        let mut values = vec![];

        for key in get.keys {
            let key_str = key.clone();

            let key_st = Key::new(key.into_bytes());
            let rv = self.cache.get(&key_st);

            match rv {
                Ok(value) => {
                    let val_st = CmdValue {
                        key: key_str,
                        flags: value.flags,
                        data: value.item.clone(),
                    };
                    values.push(val_st);
                }
                // Keys that were not found are skipped, no error given
                Err(_) => (),
            }
        }

        Resp::Values(values)
    }

    fn do_prepend(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Load the value
        let rv = self.cache.remove(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(_) => None,
                              Err(CacheError::KeyNotFound) => {
                                  Some(Resp::NotStored)
                              }
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Update the value
        let mut value = rv.unwrap();
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        // Prepend the data we just received to the blob that is there
        let mut new_item = Vec::with_capacity(set.data.len() +
                                              value.item.len());
        new_item.extend(set.data);
        new_item.extend(value.item);
        value.item = new_item;

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_replace(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Do we store this item already? If not it's an early exit.
        let rv = self.cache.contains_key(&key);
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(true) => None,
                              Ok(false) => Some(Resp::NotStored),
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_set(&mut self, set: Set) -> Resp {
        // Update stats
        self.stats.cmd_set += 1;

        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_stats(&self) -> Resp {
        let storage = self.cache.get_stats();

        let pid = get_pid().to_string();
        let uptime = ((time_now() - self.time_start) as u64).to_string();
        let time = (time_now() as u64).to_string();
        let version = get_version_string();
        let cmd_get = self.stats.cmd_get.to_string();
        let cmd_set = self.stats.cmd_set.to_string();
        let cmd_flush = self.stats.cmd_flush.to_string();
        let cmd_touch = self.stats.cmd_touch.to_string();
        let get_hits = storage.get_hits.to_string();
        let get_misses = storage.get_misses.to_string();
        let delete_hits = storage.delete_hits.to_string();
        let delete_misses = storage.delete_misses.to_string();
        let bytes_read = self.transport_stats.bytes_read.to_string();
        let bytes_written = self.transport_stats.bytes_written.to_string();
        let limit_maxbytes = self.cache.capacity.to_string();
        let bytes = storage.bytes.to_string();
        let curr_items = self.cache.len().to_string();
        let total_items = storage.total_items.to_string();
        let evictions = storage.evictions.to_string();
        let reclaimed = storage.reclaimed.to_string();

        let st_pid = Stat::new("pid", pid);
        let st_uptime = Stat::new("uptime", uptime);
        let st_time = Stat::new("time", time);
        let st_version = Stat::new("version", version);
        let st_cmd_get = Stat::new("cmd_get", cmd_get);
        let st_cmd_set = Stat::new("cmd_set", cmd_set);
        let st_cmd_flush = Stat::new("cmd_flush", cmd_flush);
        let st_cmd_touch = Stat::new("cmd_touch", cmd_touch);
        let st_get_hits = Stat::new("get_hits", get_hits);
        let st_get_misses = Stat::new("get_misses", get_misses);
        let st_delete_hits = Stat::new("delete_hits", delete_hits);
        let st_delete_misses = Stat::new("delete_misses", delete_misses);
        let st_bytes_read = Stat::new("bytes_read", bytes_read);
        let st_bytes_written = Stat::new("bytes_written", bytes_written);
        let st_limit_maxbytes = Stat::new("limit_maxbytes", limit_maxbytes);
        let st_bytes = Stat::new("bytes", bytes);
        let st_curr_items = Stat::new("curr_items", curr_items);
        let st_total_items = Stat::new("total_items", total_items);
        let st_evictions = Stat::new("evictions", evictions);
        let st_reclaimed = Stat::new("reclaimed", reclaimed);

        Resp::Stats(vec![st_pid,
                         st_uptime,
                         st_time,
                         st_version,
                         st_cmd_get,
                         st_cmd_set,
                         st_cmd_flush,
                         st_cmd_touch,
                         st_get_hits,
                         st_get_misses,
                         st_delete_hits,
                         st_delete_misses,
                         st_bytes_read,
                         st_bytes_written,
                         st_limit_maxbytes,
                         st_bytes,
                         st_curr_items,
                         st_total_items,
                         st_evictions,
                         st_reclaimed])
    }

    pub fn do_touch(&mut self, touch: Touch) -> Resp {
        // Update stats
        self.stats.cmd_touch += 1;

        let key = Key::new(touch.key.into_bytes());

        // See if the key is set
        let rv = self.cache.contains_key(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!touch.noreply,
                          match rv {
                              Ok(true) => None,
                              Ok(false) => Some(Resp::NotFound),
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Load the value
        let rv = self.cache.remove(&key);
        maybe_reply_stmt!(!touch.noreply,
                          match rv {
                              Ok(_) => None,
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Update the value
        let mut value = rv.unwrap();
        self.set_exptime(&mut value, touch.exptime);

        // Set it
        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!touch.noreply,
                          match rv {
                              Ok(_) => Resp::Touched,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    pub fn do_version(&self) -> Resp {
        Resp::Version(get_version_string())
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Delete(del) => self.do_delete(del),
            Cmd::FlushAll(flush_all) => self.do_flush_all(flush_all),
            Cmd::Get(get) => self.do_get(get),
            Cmd::Quit => Resp::Empty,  // handled at transport level
            Cmd::Set(set) => {
                match set.instr {
                    SetInstr::Add => self.do_add(set),
                    SetInstr::Append => self.do_append(set),
                    SetInstr::Prepend => self.do_prepend(set),
                    SetInstr::Replace => self.do_replace(set),
                    SetInstr::Set => self.do_set(set),
                    _ => Resp::Error,  // TODO
                }
            }
            Cmd::Stats => self.do_stats(),
            Cmd::Touch(touch) => self.do_touch(touch),
            Cmd::Version => self.do_version(),
        }
    }

    pub fn update_transport_stats(&mut self, stats: TransportStats) {
        self.transport_stats = stats;
    }
}
<commit_msg>update stats on flush<commit_after>use common::consts::get_version_string;
use platform::process::get_pid;
use platform::time::time_now;
use storage::Cache;
use storage::CacheError;
use storage::Key;
use storage::Value;
use tcp_transport::stats::TransportStats;

use super::cmd::Cmd;
use super::cmd::Delete;
use super::cmd::FlushAll;
use super::cmd::Get;
use super::cmd::Resp;
use super::cmd::Set;
use super::cmd::SetInstr;
use super::cmd::Stat;
use super::cmd::Touch;
use super::cmd::Value as CmdValue;
use super::util::convert_exptime;
use super::util::from_cache_err;

// For use to get an early exit from a function. The first parameter is a bool
// to indicate whether to omit responses (returns Resp::Empty instead). The
// second parameter is an expression that evaluates to Option<Resp>. In case
// of None no return is made ie. execution continues. In case of Some(resp) we
// perform return resp;
//
// maybe_reply_stmt!(!noreply, match rv {
//     Ok(_) => None,   // do not return
//     Err(_) => Some(Resp::Error),
// });
//
// ->
//
// let opt = expr_provided;
// if !opt.is_none() {
//     if !noreply {
//         return opt.unwrap();
//     } else {
//         return Resp::Empty;
//     }
// }
macro_rules! maybe_reply_stmt {
    ( $cond:expr, $opt:expr ) => {
        {
            if !$opt.is_none() {
                if $cond {
                    return $opt.unwrap();
                } else {
                    return Resp::Empty;
                }
            };
        }
    };
}

// For use to conditionally produce a return value. The first parameter is used
// to indicate whether to omit responses (returns Resp::Empty instead). The
// second parameter is an expression that evaluates to Resp.
//
// maybe_reply_expr!(!noreply, match rv {
//     Ok(_) => Resp::Stored,
//     Err(_) => Resp::Error,
// });
//
// ->
//
// let resp = expr_provided;
// if !noreply {
//     resp
// } else {
//     Resp::Empty
// }
macro_rules! maybe_reply_expr {
    ( $cond:expr, $resp:expr ) => {
        {
            if $cond {
                $resp
            } else {
                Resp::Empty
            }
        }
    };
}

struct DriverStats {
    cmd_get: u64,
    cmd_set: u64,
    cmd_flush: u64,
    cmd_touch: u64,
}

impl DriverStats {
    pub fn new() -> DriverStats {
        DriverStats {
            cmd_get: 0,
            cmd_set: 0,
            cmd_flush: 0,
            cmd_touch: 0,
        }
    }
}

pub struct Driver {
    cache: Cache,
    time_start: f64,

    stats: DriverStats,
    transport_stats: TransportStats, // this is a global snapshot
}

impl Driver {
    pub fn new(cache: Cache) -> Driver {
        Driver {
            cache: cache,
            stats: DriverStats::new(),
            time_start: time_now(),
            transport_stats: TransportStats::new(),
        }
    }

    fn set_exptime(&self, value: &mut Value, exptime: u32) {
        // If exptime is greater than zero we need to set it on the value
        if exptime > 0 {
            let tm;

            // Is it an interval greater than 30 days? Then it's a timestamp
            if exptime > 60 * 60 * 24 * 30 {
                tm = exptime as f64;

            } else {
                // Otherwise it's relative from now
                tm = time_now() + exptime as f64;
            }

            value.set_exptime(tm);
        }
    }

    fn do_add(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Do we store this item already? If so it's an early exit.
        let rv = self.cache.contains_key(&key);
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(true) => Some(Resp::NotStored),
                              Ok(false) => None,
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_append(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Load the value
        let rv = self.cache.remove(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Err(CacheError::KeyNotFound) => {
                                  Some(Resp::NotStored)
                              }
                              Err(ref err) => Some(from_cache_err(err)),
                              _ => None,
                          });

        // Update the value
        let mut value = rv.unwrap();
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        // Append the data we just received to the blob that is there
        value.item.extend(set.data);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_delete(&mut self, delete: Delete) -> Resp {
        let key = Key::new(delete.key.clone().into_bytes());

        let rv = self.cache.remove(&key);

        maybe_reply_expr!(!delete.noreply,
                          match rv {
                              Ok(_) => Resp::Deleted,
                              Err(CacheError::KeyNotFound) => Resp::NotFound,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_flush_all(&mut self, flush_all: FlushAll) -> Resp {
        // Update stats
        self.stats.cmd_flush += 1;

        let exptime: f64 = match flush_all.exptime {
            Some(exptime) => convert_exptime(flush_all.exptime.unwrap()).unwrap(),
            None => time_now(),
        };

        let rv = self.cache.flush_all(exptime);

        maybe_reply_expr!(!flush_all.noreply,
                          match rv {
                              Ok(_) => Resp::Ok,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_get(&mut self, get: Get) -> Resp {
        // Update stats
        self.stats.cmd_get += 1;

        let mut values = vec![];

        for key in get.keys {
            let key_str = key.clone();

            let key_st = Key::new(key.into_bytes());
            let rv = self.cache.get(&key_st);

            match rv {
                Ok(value) => {
                    let val_st = CmdValue {
                        key: key_str,
                        flags: value.flags,
                        data: value.item.clone(),
                    };
                    values.push(val_st);
                }
                // Keys that were not found are skipped, no error given
                Err(_) => (),
            }
        }

        Resp::Values(values)
    }

    fn do_prepend(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Load the value
        let rv = self.cache.remove(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(_) => None,
                              Err(CacheError::KeyNotFound) => {
                                  Some(Resp::NotStored)
                              }
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Update the value
        let mut value = rv.unwrap();
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        // Prepend the data we just received to the blob that is there
        let mut new_item = Vec::with_capacity(set.data.len() +
                                              value.item.len());
        new_item.extend(set.data);
        new_item.extend(value.item);
        value.item = new_item;

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_replace(&mut self, set: Set) -> Resp {
        let key = Key::new(set.key.into_bytes());

        // Do we store this item already? If not it's an early exit.
        let rv = self.cache.contains_key(&key);
        maybe_reply_stmt!(!set.noreply,
                          match rv {
                              Ok(true) => None,
                              Ok(false) => Some(Resp::NotStored),
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_set(&mut self, set: Set) -> Resp {
        // Update stats
        self.stats.cmd_set += 1;

        let key = Key::new(set.key.into_bytes());
        let mut value = Value::new(set.data);
        value.with_flags(set.flags);
        self.set_exptime(&mut value, set.exptime);

        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!set.noreply,
                          match rv {
                              Ok(_) => Resp::Stored,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    fn do_stats(&self) -> Resp {
        let storage = self.cache.get_stats();

        let pid = get_pid().to_string();
        let uptime = ((time_now() - self.time_start) as u64).to_string();
        let time = (time_now() as u64).to_string();
        let version = get_version_string();
        let cmd_get = self.stats.cmd_get.to_string();
        let cmd_set = self.stats.cmd_set.to_string();
        let cmd_flush = self.stats.cmd_flush.to_string();
        let cmd_touch = self.stats.cmd_touch.to_string();
        let get_hits = storage.get_hits.to_string();
        let get_misses = storage.get_misses.to_string();
        let delete_hits = storage.delete_hits.to_string();
        let delete_misses = storage.delete_misses.to_string();
        let bytes_read = self.transport_stats.bytes_read.to_string();
        let bytes_written = self.transport_stats.bytes_written.to_string();
        let limit_maxbytes = self.cache.capacity.to_string();
        let bytes = storage.bytes.to_string();
        let curr_items = self.cache.len().to_string();
        let total_items = storage.total_items.to_string();
        let evictions = storage.evictions.to_string();
        let reclaimed = storage.reclaimed.to_string();

        let st_pid = Stat::new("pid", pid);
        let st_uptime = Stat::new("uptime", uptime);
        let st_time = Stat::new("time", time);
        let st_version = Stat::new("version", version);
        let st_cmd_get = Stat::new("cmd_get", cmd_get);
        let st_cmd_set = Stat::new("cmd_set", cmd_set);
        let st_cmd_flush = Stat::new("cmd_flush", cmd_flush);
        let st_cmd_touch = Stat::new("cmd_touch", cmd_touch);
        let st_get_hits = Stat::new("get_hits", get_hits);
        let st_get_misses = Stat::new("get_misses", get_misses);
        let st_delete_hits = Stat::new("delete_hits", delete_hits);
        let st_delete_misses = Stat::new("delete_misses", delete_misses);
        let st_bytes_read = Stat::new("bytes_read", bytes_read);
        let st_bytes_written = Stat::new("bytes_written", bytes_written);
        let st_limit_maxbytes = Stat::new("limit_maxbytes", limit_maxbytes);
        let st_bytes = Stat::new("bytes", bytes);
        let st_curr_items = Stat::new("curr_items", curr_items);
        let st_total_items = Stat::new("total_items", total_items);
        let st_evictions = Stat::new("evictions", evictions);
        let st_reclaimed = Stat::new("reclaimed", reclaimed);

        Resp::Stats(vec![st_pid,
                         st_uptime,
                         st_time,
                         st_version,
                         st_cmd_get,
                         st_cmd_set,
                         st_cmd_flush,
                         st_cmd_touch,
                         st_get_hits,
                         st_get_misses,
                         st_delete_hits,
                         st_delete_misses,
                         st_bytes_read,
                         st_bytes_written,
                         st_limit_maxbytes,
                         st_bytes,
                         st_curr_items,
                         st_total_items,
                         st_evictions,
                         st_reclaimed])
    }

    pub fn do_touch(&mut self, touch: Touch) -> Resp {
        // Update stats
        self.stats.cmd_touch += 1;

        let key = Key::new(touch.key.into_bytes());

        // See if the key is set
        let rv = self.cache.contains_key(&key);

        // If if it's not there we error out
        maybe_reply_stmt!(!touch.noreply,
                          match rv {
                              Ok(true) => None,
                              Ok(false) => Some(Resp::NotFound),
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Load the value
        let rv = self.cache.remove(&key);
        maybe_reply_stmt!(!touch.noreply,
                          match rv {
                              Ok(_) => None,
                              Err(ref err) => Some(from_cache_err(err)),
                          });

        // Update the value
        let mut value = rv.unwrap();
        self.set_exptime(&mut value, touch.exptime);

        // Set it
        let rv = self.cache.set(key, value);

        maybe_reply_expr!(!touch.noreply,
                          match rv {
                              Ok(_) => Resp::Touched,
                              Err(ref err) => from_cache_err(err),
                          })
    }

    pub fn do_version(&self) -> Resp {
        Resp::Version(get_version_string())
    }

    pub fn run(&mut self, cmd: Cmd) -> Resp {
        match cmd {
            Cmd::Delete(del) => self.do_delete(del),
            Cmd::FlushAll(flush_all) => self.do_flush_all(flush_all),
            Cmd::Get(get) => self.do_get(get),
            Cmd::Quit => Resp::Empty,  // handled at transport level
            Cmd::Set(set) => {
                match set.instr {
                    SetInstr::Add => self.do_add(set),
                    SetInstr::Append => self.do_append(set),
                    SetInstr::Prepend => self.do_prepend(set),
                    SetInstr::Replace => self.do_replace(set),
                    SetInstr::Set => self.do_set(set),
                    _ => Resp::Error,  // TODO
                }
            }
            Cmd::Stats => self.do_stats(),
            Cmd::Touch(touch) => self.do_touch(touch),
            Cmd::Version => self.do_version(),
        }
    }

    pub fn update_transport_stats(&mut self, stats: TransportStats) {
        self.transport_stats = stats;
    }
}
<|endoftext|>


<commit_before>extern crate actix;
extern crate actix_http;
extern crate actix_net;
extern crate actix_web;
extern crate bytes;
extern crate futures;

use std::{io::Read, io::Write, net, thread, time};

use actix::System;
use actix_net::server::Server;
use actix_web::{client, test, HttpMessage};
use bytes::Bytes;
use futures::future::{self, ok};
use futures::stream::once;

use actix_http::{h1, Body, KeepAlive, Request, Response};

#[test]
fn test_h1_v2() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::build()
                    .keep_alive(KeepAlive::Disabled)
                    .client_timeout(1000)
                    .client_disconnect(1000)
                    .server_hostname("localhost")
                    .server_address(addr)
                    .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    {
        let req = client::ClientRequest::get(format!("http://{}/", addr))
            .finish()
            .unwrap();
        let response = sys.block_on(req.send()).unwrap();
        assert!(response.status().is_success());
    }
}

#[test]
fn test_slow_request() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::build()
                    .client_timeout(100)
                    .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut stream = net::TcpStream::connect(addr).unwrap();
    let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n");
    let mut data = String::new();
    let _ = stream.read_to_string(&mut data);
    assert!(data.starts_with("HTTP/1.1 408 Request Timeout"));
}

#[test]
fn test_malformed_request() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut stream = net::TcpStream::connect(addr).unwrap();
    let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n");
    let mut data = String::new();
    let _ = stream.read_to_string(&mut data);
    assert!(data.starts_with("HTTP/1.1 400 Bad Request"));
}

#[test]
fn test_content_length() {
    use actix_http::http::{
        header::{HeaderName, HeaderValue},
        StatusCode,
    };

    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|req: Request| {
                    let indx: usize = req.uri().path()[1..].parse().unwrap();
                    let statuses = [
                        StatusCode::NO_CONTENT,
                        StatusCode::CONTINUE,
                        StatusCode::SWITCHING_PROTOCOLS,
                        StatusCode::PROCESSING,
                        StatusCode::OK,
                        StatusCode::NOT_FOUND,
                    ];
                    future::ok::<_, ()>(Response::new(statuses[indx]))
                })
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let header = HeaderName::from_static("content-length");
    let value = HeaderValue::from_static("0");

    let mut sys = System::new("test");
    {
        for i in 0..4 {
            let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), None);

            let req = client::ClientRequest::head(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), None);
        }

        for i in 4..6 {
            let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), Some(&value));
        }
    }
}

#[test]
fn test_headers() {
    let data = STR.repeat(10);
    let data2 = data.clone();

    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                let data = data.clone();
                h1::H1Service::new(move |_| {
                    let mut builder = Response::Ok();
                    for idx in 0..90 {
                        builder.header(
                            format!("X-TEST-{}", idx).as_str(),
                            "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
                        );
                    }
                    future::ok::<_, ()>(builder.body(data.clone()))
                })
            })
            .unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(400));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();

    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from(data2));
}

const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World";

#[test]
fn test_body() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_head_empty() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).finish())
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert!(bytes.is_empty());
}

#[test]
fn test_head_binary() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    ok::<_, ()>(
                        Response::Ok().content_length(STR.len() as u64).body(STR),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert!(bytes.is_empty());
}

#[test]
fn test_head_binary2() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR)))
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }
}

#[test]
fn test_body_length() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(
                        Response::Ok()
                            .content_length(STR.len() as u64)
                            .body(Body::Streaming(Box::new(body))),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_body_chunked_explicit() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(
                        Response::Ok()
                            .chunked()
                            .body(Body::Streaming(Box::new(body))),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();

    // decode
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_body_chunked_implicit() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(Response::Ok().body(Body::Streaming(Box::new(body))))
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}
<commit_msg>fix tests on stable<commit_after>extern crate actix;
extern crate actix_http;
extern crate actix_net;
extern crate actix_web;
extern crate bytes;
extern crate futures;

use std::{io::Read, io::Write, net, thread, time};

use actix::System;
use actix_net::server::Server;
use actix_web::{client, test, HttpMessage};
use bytes::Bytes;
use futures::future::{self, ok};
use futures::stream::once;

use actix_http::{h1, http, Body, KeepAlive, Request, Response};

#[test]
fn test_h1_v2() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::build()
                    .keep_alive(KeepAlive::Disabled)
                    .client_timeout(1000)
                    .client_disconnect(1000)
                    .server_hostname("localhost")
                    .server_address(addr)
                    .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    {
        let req = client::ClientRequest::get(format!("http://{}/", addr))
            .finish()
            .unwrap();
        let response = sys.block_on(req.send()).unwrap();
        assert!(response.status().is_success());
    }
}

#[test]
fn test_slow_request() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::build()
                    .client_timeout(100)
                    .finish(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut stream = net::TcpStream::connect(addr).unwrap();
    let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n");
    let mut data = String::new();
    let _ = stream.read_to_string(&mut data);
    assert!(data.starts_with("HTTP/1.1 408 Request Timeout"));
}

#[test]
fn test_malformed_request() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().finish()))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut stream = net::TcpStream::connect(addr).unwrap();
    let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n");
    let mut data = String::new();
    let _ = stream.read_to_string(&mut data);
    assert!(data.starts_with("HTTP/1.1 400 Bad Request"));
}

#[test]
fn test_content_length() {
    use actix_http::http::{
        header::{HeaderName, HeaderValue},
        StatusCode,
    };

    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|req: Request| {
                    let indx: usize = req.uri().path()[1..].parse().unwrap();
                    let statuses = [
                        StatusCode::NO_CONTENT,
                        StatusCode::CONTINUE,
                        StatusCode::SWITCHING_PROTOCOLS,
                        StatusCode::PROCESSING,
                        StatusCode::OK,
                        StatusCode::NOT_FOUND,
                    ];
                    future::ok::<_, ()>(Response::new(statuses[indx]))
                })
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let header = HeaderName::from_static("content-length");
    let value = HeaderValue::from_static("0");

    let mut sys = System::new("test");
    {
        for i in 0..4 {
            let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), None);

            let req = client::ClientRequest::head(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), None);
        }

        for i in 4..6 {
            let req = client::ClientRequest::get(format!("http://{}/{}", addr, i))
                .finish()
                .unwrap();
            let response = sys.block_on(req.send()).unwrap();
            assert_eq!(response.headers().get(&header), Some(&value));
        }
    }
}

#[test]
fn test_headers() {
    let data = STR.repeat(10);
    let data2 = data.clone();

    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                let data = data.clone();
                h1::H1Service::new(move |_| {
                    let mut builder = Response::Ok();
                    for idx in 0..90 {
                        builder.header(
                            format!("X-TEST-{}", idx).as_str(),
                            "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \
                             TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
                        );
                    }
                    future::ok::<_, ()>(builder.body(data.clone()))
                })
            })
            .unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(400));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();

    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from(data2));
}

const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World \
                   Hello World Hello World Hello World Hello World Hello World";

#[test]
fn test_body() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| future::ok::<_, ()>(Response::Ok().body(STR)))
            }).unwrap()
            .run();
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_head_empty() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).finish())
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert!(bytes.is_empty());
}

#[test]
fn test_head_binary() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    ok::<_, ()>(
                        Response::Ok().content_length(STR.len() as u64).body(STR),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert!(bytes.is_empty());
}

#[test]
fn test_head_binary2() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| ok::<_, ()>(Response::Ok().body(STR)))
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::head(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    {
        let len = response
            .headers()
            .get(http::header::CONTENT_LENGTH)
            .unwrap();
        assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
    }
}

#[test]
fn test_body_length() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(
                        Response::Ok()
                            .content_length(STR.len() as u64)
                            .body(Body::Streaming(Box::new(body))),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_body_chunked_explicit() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(
                        Response::Ok()
                            .chunked()
                            .body(Body::Streaming(Box::new(body))),
                    )
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();

    // decode
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}

#[test]
fn test_body_chunked_implicit() {
    let addr = test::TestServer::unused_addr();
    thread::spawn(move || {
        Server::new()
            .bind("test", addr, move || {
                h1::H1Service::new(|_| {
                    let body = once(Ok(Bytes::from_static(STR.as_ref())));
                    ok::<_, ()>(Response::Ok().body(Body::Streaming(Box::new(body))))
                })
            }).unwrap()
            .run()
    });
    thread::sleep(time::Duration::from_millis(100));

    let mut sys = System::new("test");
    let req = client::ClientRequest::get(format!("http://{}/", addr))
        .finish()
        .unwrap();
    let response = sys.block_on(req.send()).unwrap();
    assert!(response.status().is_success());

    // read response
    let bytes = sys.block_on(response.body()).unwrap();
    assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
}
<|endoftext|>


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

use hlua::any::AnyLuaValue;

use super::*;

const EVENT: &'static str = include_str!("event.lua");
const TEST: &'static str = include_str!("test.lua");
const REQUIRE: &'static str = include_str!("require.lua");

/// Call Engine.new without any code.
#[test]
fn engine_new_no_code() {
    Engine::new(HashMap::new());
}

/// Tests requiring a module.
#[test]
fn load_module() {
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("test".to_owned(), TEST.to_owned());
    scripts.insert("init".to_owned(), REQUIRE.to_owned());
    Engine::new(scripts);
}

/// Tests declaring and raising a lua event.
#[test]
fn lua_event() {
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("init".to_owned(), EVENT.to_owned());
    let mut engine = Engine::new(scripts);
    let _ = engine.exec_event("test".to_owned(), Vec::new()).expect("Failed to exec event");
    let test_val: AnyLuaValue = engine.interpreter.get("test_val").unwrap();
    assert_eq!(test_val, AnyLuaValue::LuaBoolean(true));
}
<commit_msg>Adds a test for script::Engine::call_prelude_fn when there are 0 arguments and no returns<commit_after>use std::sync::atomic::{AtomicBool, Ordering};
use std::collections::HashMap;

use hlua::any::AnyLuaValue;
use hlua::{LuaTable, function0};

use super::*;

const EVENT: &'static str = include_str!("event.lua");
const TEST: &'static str = include_str!("test.lua");
const REQUIRE: &'static str = include_str!("require.lua");

static CALL_FN_NO_ARGS_TEST_VAL: AtomicBool = AtomicBool::new(false);

/// Call Engine.new without any code.
#[test]
fn engine_new_no_code() {
    Engine::new(HashMap::new());
}

/// Tests requiring a module.
#[test]
fn load_module() {
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("test".to_owned(), TEST.to_owned());
    scripts.insert("init".to_owned(), REQUIRE.to_owned());
    Engine::new(scripts);
}

/// Tests declaring and raising a lua event.
#[test]
fn lua_event() {
    let mut scripts: HashMap<String, String> = HashMap::new();
    scripts.insert("init".to_owned(), EVENT.to_owned());
    let mut engine = Engine::new(scripts);
    let _ = engine.exec_event("test".to_owned(), Vec::new()).expect("Failed to exec event");
    let test_val: AnyLuaValue = engine.interpreter.get("test_val").unwrap();
    assert_eq!(test_val, AnyLuaValue::LuaBoolean(true));
}

/// Tests calling a prelude function using Engine::call_prelude_fn.
///
/// Curently broken until tomaka/hlua#66
#[test]
fn call_fn_no_args() {
    let mut engine = Engine::new(HashMap::new());
    let fun = function0(|| {
        CALL_FN_NO_ARGS_TEST_VAL.store(true, Ordering::Relaxed);
    });
    {
        let mut prelude_table: LuaTable<_> = engine.interpreter
                                                   .get("prelude_buildengine")
                                                   .expect("Failed to get prelude table.");
        prelude_table.set("test_fn", fun);
    }
    let result = engine.call_prelude_fn("test_fn", Vec::new()).expect("Failed to call test_fn");
    assert!(result.is_none(),
            "Engine::call_prelude_fn returned a Some value for a function returning nil: {:?}",
            result);
}
<|endoftext|>