<commit_before>//! Endpoints for event filters.

pub mod create_filter;
pub mod get_filter;

use std::fmt;

use js_int::UInt;
use ruma_api::Outgoing;
use ruma_identifiers::{RoomId, UserId};
use serde::{
    de::{MapAccess, Visitor},
    ser::SerializeStruct,
    Deserialize, Deserializer, Serialize, Serializer,
};

/// Format to use for returned events
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EventFormat {
    /// Client format, as described in the Client API.
    Client,

    /// Raw events from federation.
    Federation,
}

/// Filters to be applied to room events
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct RoomEventFilter<'a> {
    /// A list of event types to exclude.
    ///
    /// If this list is absent then no event types are excluded. A matching type will be excluded
    /// even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any
    /// sequence of characters.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_types: &'a [String],

    /// A list of room IDs to exclude.
    ///
    /// If this list is absent then no rooms are excluded. A matching room will be excluded even if
    /// it is listed in the 'rooms' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_rooms: &'a [String],

    /// The maximum number of events to return.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<UInt>,

    /// A list of room IDs to include.
    ///
    /// If this list is absent then all rooms are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rooms: Option<&'a [RoomId]>,

    /// A list of sender IDs to exclude.
    ///
    /// If this list is absent then no senders are excluded. A matching sender will be excluded even
    /// if it is listed in the 'senders' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_senders: &'a [UserId],

    /// A list of senders IDs to include.
    ///
    /// If this list is absent then all senders are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub senders: Option<&'a [UserId]>,

    /// A list of event types to include.
    ///
    /// If this list is absent then all event types are included. A '*' can be used as a wildcard to
    /// match any sequence of characters.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub types: Option<&'a [String]>,

    /// Controls whether to include events with a URL key in their content.
    ///
    /// * `None`: No filtering
    /// * `Some(true)`: Only events with a URL
    /// * `Some(false)`: Only events without a URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub contains_url: Option<bool>,

    /// Options to control lazy-loading of membership events.
    #[serde(flatten)]
    pub lazy_load_options: LazyLoadOptions,
}

impl<'a> RoomEventFilter<'a> {
    /// A filter that can be used to ignore all room events
    pub fn ignore_all() -> Self {
        Self { types: Some(&[]), ..Default::default() }
    }
}

/// Filters to be applied to room data
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct RoomFilter<'a> {
    /// Include rooms that the user has left in the sync.
    ///
    /// Defaults to `false`.
    #[serde(default, skip_serializing_if = "ruma_serde::is_default")]
    pub include_leave: bool,

    /// The per user account data to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_data: Option<RoomEventFilter<'a>>,

    /// The message and state update events to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeline: Option<RoomEventFilter<'a>>,

    /// The events that aren't recorded in the room history, e.g. typing and receipts, to include
    /// for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ephemeral: Option<RoomEventFilter<'a>>,

    /// The state events to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<RoomEventFilter<'a>>,

    /// A list of room IDs to exclude.
    ///
    /// If this list is absent then no rooms are excluded. A matching room will be excluded even if
    /// it is listed in the 'rooms' filter. This filter is applied before the filters in
    /// `ephemeral`, `state`, `timeline` or `account_data`.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_rooms: &'a [RoomId],

    /// A list of room IDs to include.
    ///
    /// If this list is absent then all rooms are included. This filter is applied before the
    /// filters in `ephemeral`, `state`, `timeline` or `account_data`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rooms: Option<&'a [RoomId]>,
}

impl<'a> RoomFilter<'a> {
    /// A filter that can be used to ignore all room events (of any type)
    pub fn ignore_all() -> Self {
        Self { rooms: Some(&[]), ..Default::default() }
    }
}

/// Filter for not-room data
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct Filter<'a> {
    /// A list of event types to exclude.
    ///
    /// If this list is absent then no event types are excluded. A matching type will be excluded
    /// even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any
    /// sequence of characters.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_types: &'a [String],

    /// The maximum number of events to return.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<UInt>,

    /// A list of senders IDs to include.
    ///
    /// If this list is absent then all senders are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub senders: Option<&'a [UserId]>,

    /// A list of event types to include.
    ///
    /// If this list is absent then all event types are included. A '*' can be used as a wildcard to
    /// match any sequence of characters.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub types: Option<&'a [String]>,

    /// A list of sender IDs to exclude.
    ///
    /// If this list is absent then no senders are excluded. A matching sender will be excluded even
    /// if it is listed in the 'senders' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_senders: &'a [UserId],
}

impl<'a> Filter<'a> {
    /// A filter that can be used to ignore all events
    pub fn ignore_all() -> Self {
        Self { types: Some(&[]), ..Default::default() }
    }
}

/// A filter definition
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct FilterDefinition<'a> {
    /// List of event fields to include.
    ///
    /// If this list is absent then all fields are included. The entries may include '.' charaters
    /// to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content'
    /// object. A literal '.' character in a field name may be escaped using a '\'. A server may
    /// include more fields than were requested.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_fields: Option<&'a [String]>,

    /// The format to use for events.
    ///
    /// 'client' will return the events in a format suitable for clients. 'federation' will return
    /// the raw event as received over federation. The default is 'client'.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_format: Option<EventFormat>,

    /// The presence updates to include.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presence: Option<Filter<'a>>,

    /// The user account data that isn't associated with rooms to include.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_data: Option<Filter<'a>>,

    /// Filters to be applied to room data.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room: Option<RoomFilter<'a>>,
}

impl<'a> FilterDefinition<'a> {
    /// A filter that can be used to ignore all events
    pub fn ignore_all() -> Self {
        Self {
            account_data: Some(Filter::ignore_all()),
            room: Some(RoomFilter::ignore_all()),
            presence: Some(Filter::ignore_all()),
            ..Default::default()
        }
    }
}

/// Specifies options for [lazy-loading membership events][lazy-loading] on
/// supported endpoints
///
/// [lazy-loading]: https://matrix.org/docs/spec/client_server/r0.6.0#lazy-loading-room-members
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LazyLoadOptions {
    /// Disables lazy-loading of membership events.
    Disabled,

    /// Enables lazy-loading of events.
    Enabled {
        /// If `true`, sends all membership events for all events, even if they have
        /// already been sent to the client. Defaults to `false`.
        include_redundant_members: bool,
    },
}

impl Serialize for LazyLoadOptions {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state;
        match *self {
            Self::Enabled { include_redundant_members: true } => {
                state = serializer.serialize_struct("LazyLoad", 2)?;
                state.serialize_field("lazy_load_members", &true)?;
                state.serialize_field("include_redundant_members", &true)?;
            }
            Self::Enabled { .. } => {
                state = serializer.serialize_struct("LazyLoad", 1)?;
                state.serialize_field("lazy_load_members", &true)?;
            }
            _ => {
                state = serializer.serialize_struct("LazyLoad", 0)?;
            }
        }
        state.end()
    }
}

impl Default for LazyLoadOptions {
    fn default() -> Self {
        Self::Disabled
    }
}

struct LazyLoadOptionsVisitor;

impl<'de> Visitor<'de> for LazyLoadOptionsVisitor {
    type Value = LazyLoadOptions;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("Lazy load options")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut lazy_load_members = false;
        let mut include_redundant_members = false;
        while let Some((key, value)) = access.next_entry::<String, bool>()? {
            match &*key {
                "lazy_load_members" => lazy_load_members = value,
                "include_redundant_members" => include_redundant_members = value,
                _ => {}
            };
        }

        Ok(if lazy_load_members {
            LazyLoadOptions::Enabled { include_redundant_members }
        } else {
            LazyLoadOptions::Disabled
        })
    }
}

impl<'de> Deserialize<'de> for LazyLoadOptions {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(LazyLoadOptionsVisitor)
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::LazyLoadOptions;

    #[test]
    fn test_serializing_disabled_lazy_load() {
        let lazy_load_options = LazyLoadOptions::Disabled;
        assert_eq!(to_json_value(lazy_load_options).unwrap(), json!({}));
    }

    #[test]
    fn test_serializing_lazy_load_no_redundant() {
        let lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: false };
        assert_eq!(to_json_value(lazy_load_options).unwrap(), json!({ "lazy_load_members": true }));
    }

    #[test]
    fn test_serializing_lazy_load_with_redundant() {
        let lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: true };
        assert_eq!(
            to_json_value(lazy_load_options).unwrap(),
            json!({ "lazy_load_members": true, "include_redundant_members": true })
        );
    }

    #[test]
    fn test_deserializing_no_lazy_load() {
        let json = json!({});
        assert_eq!(from_json_value::<LazyLoadOptions>(json).unwrap(), LazyLoadOptions::Disabled,);
    }

    #[test]
    fn test_deserializing_ignore_redundant_members_when_no_lazy_load() {
        let json = json!({ "include_redundant_members": true });
        assert_eq!(from_json_value::<LazyLoadOptions>(json).unwrap(), LazyLoadOptions::Disabled,);
    }
}
<commit_msg>Fix a typo<commit_after>//! Endpoints for event filters.

pub mod create_filter;
pub mod get_filter;

use std::fmt;

use js_int::UInt;
use ruma_api::Outgoing;
use ruma_identifiers::{RoomId, UserId};
use serde::{
    de::{MapAccess, Visitor},
    ser::SerializeStruct,
    Deserialize, Deserializer, Serialize, Serializer,
};

/// Format to use for returned events
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EventFormat {
    /// Client format, as described in the Client API.
    Client,

    /// Raw events from federation.
    Federation,
}

/// Filters to be applied to room events
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct RoomEventFilter<'a> {
    /// A list of event types to exclude.
    ///
    /// If this list is absent then no event types are excluded. A matching type will be excluded
    /// even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any
    /// sequence of characters.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_types: &'a [String],

    /// A list of room IDs to exclude.
    ///
    /// If this list is absent then no rooms are excluded. A matching room will be excluded even if
    /// it is listed in the 'rooms' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_rooms: &'a [String],

    /// The maximum number of events to return.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<UInt>,

    /// A list of room IDs to include.
    ///
    /// If this list is absent then all rooms are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rooms: Option<&'a [RoomId]>,

    /// A list of sender IDs to exclude.
    ///
    /// If this list is absent then no senders are excluded. A matching sender will be excluded even
    /// if it is listed in the 'senders' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_senders: &'a [UserId],

    /// A list of senders IDs to include.
    ///
    /// If this list is absent then all senders are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub senders: Option<&'a [UserId]>,

    /// A list of event types to include.
    ///
    /// If this list is absent then all event types are included. A '*' can be used as a wildcard to
    /// match any sequence of characters.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub types: Option<&'a [String]>,

    /// Controls whether to include events with a URL key in their content.
    ///
    /// * `None`: No filtering
    /// * `Some(true)`: Only events with a URL
    /// * `Some(false)`: Only events without a URL
    #[serde(skip_serializing_if = "Option::is_none")]
    pub contains_url: Option<bool>,

    /// Options to control lazy-loading of membership events.
    #[serde(flatten)]
    pub lazy_load_options: LazyLoadOptions,
}

impl<'a> RoomEventFilter<'a> {
    /// A filter that can be used to ignore all room events
    pub fn ignore_all() -> Self {
        Self { types: Some(&[]), ..Default::default() }
    }
}

/// Filters to be applied to room data
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct RoomFilter<'a> {
    /// Include rooms that the user has left in the sync.
    ///
    /// Defaults to `false`.
    #[serde(default, skip_serializing_if = "ruma_serde::is_default")]
    pub include_leave: bool,

    /// The per user account data to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_data: Option<RoomEventFilter<'a>>,

    /// The message and state update events to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeline: Option<RoomEventFilter<'a>>,

    /// The events that aren't recorded in the room history, e.g. typing and receipts, to include
    /// for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ephemeral: Option<RoomEventFilter<'a>>,

    /// The state events to include for rooms.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub state: Option<RoomEventFilter<'a>>,

    /// A list of room IDs to exclude.
    ///
    /// If this list is absent then no rooms are excluded. A matching room will be excluded even if
    /// it is listed in the 'rooms' filter. This filter is applied before the filters in
    /// `ephemeral`, `state`, `timeline` or `account_data`.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_rooms: &'a [RoomId],

    /// A list of room IDs to include.
    ///
    /// If this list is absent then all rooms are included. This filter is applied before the
    /// filters in `ephemeral`, `state`, `timeline` or `account_data`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub rooms: Option<&'a [RoomId]>,
}

impl<'a> RoomFilter<'a> {
    /// A filter that can be used to ignore all room events (of any type)
    pub fn ignore_all() -> Self {
        Self { rooms: Some(&[]), ..Default::default() }
    }
}

/// Filter for not-room data
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct Filter<'a> {
    /// A list of event types to exclude.
    ///
    /// If this list is absent then no event types are excluded. A matching type will be excluded
    /// even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any
    /// sequence of characters.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_types: &'a [String],

    /// The maximum number of events to return.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<UInt>,

    /// A list of senders IDs to include.
    ///
    /// If this list is absent then all senders are included.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub senders: Option<&'a [UserId]>,

    /// A list of event types to include.
    ///
    /// If this list is absent then all event types are included. A '*' can be used as a wildcard to
    /// match any sequence of characters.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub types: Option<&'a [String]>,

    /// A list of sender IDs to exclude.
    ///
    /// If this list is absent then no senders are excluded. A matching sender will be excluded even
    /// if it is listed in the 'senders' filter.
    #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
    pub not_senders: &'a [UserId],
}

impl<'a> Filter<'a> {
    /// A filter that can be used to ignore all events
    pub fn ignore_all() -> Self {
        Self { types: Some(&[]), ..Default::default() }
    }
}

/// A filter definition
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[incoming_derive(Clone, Serialize)]
pub struct FilterDefinition<'a> {
    /// List of event fields to include.
    ///
    /// If this list is absent then all fields are included. The entries may include '.' characters
    /// to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content'
    /// object. A literal '.' character in a field name may be escaped using a '\'. A server may
    /// include more fields than were requested.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_fields: Option<&'a [String]>,

    /// The format to use for events.
    ///
    /// 'client' will return the events in a format suitable for clients. 'federation' will return
    /// the raw event as received over federation. The default is 'client'.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_format: Option<EventFormat>,

    /// The presence updates to include.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub presence: Option<Filter<'a>>,

    /// The user account data that isn't associated with rooms to include.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub account_data: Option<Filter<'a>>,

    /// Filters to be applied to room data.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room: Option<RoomFilter<'a>>,
}

impl<'a> FilterDefinition<'a> {
    /// A filter that can be used to ignore all events
    pub fn ignore_all() -> Self {
        Self {
            account_data: Some(Filter::ignore_all()),
            room: Some(RoomFilter::ignore_all()),
            presence: Some(Filter::ignore_all()),
            ..Default::default()
        }
    }
}

/// Specifies options for [lazy-loading membership events][lazy-loading] on
/// supported endpoints
///
/// [lazy-loading]: https://matrix.org/docs/spec/client_server/r0.6.0#lazy-loading-room-members
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LazyLoadOptions {
    /// Disables lazy-loading of membership events.
    Disabled,

    /// Enables lazy-loading of events.
    Enabled {
        /// If `true`, sends all membership events for all events, even if they have
        /// already been sent to the client. Defaults to `false`.
        include_redundant_members: bool,
    },
}

impl Serialize for LazyLoadOptions {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state;
        match *self {
            Self::Enabled { include_redundant_members: true } => {
                state = serializer.serialize_struct("LazyLoad", 2)?;
                state.serialize_field("lazy_load_members", &true)?;
                state.serialize_field("include_redundant_members", &true)?;
            }
            Self::Enabled { .. } => {
                state = serializer.serialize_struct("LazyLoad", 1)?;
                state.serialize_field("lazy_load_members", &true)?;
            }
            _ => {
                state = serializer.serialize_struct("LazyLoad", 0)?;
            }
        }
        state.end()
    }
}

impl Default for LazyLoadOptions {
    fn default() -> Self {
        Self::Disabled
    }
}

struct LazyLoadOptionsVisitor;

impl<'de> Visitor<'de> for LazyLoadOptionsVisitor {
    type Value = LazyLoadOptions;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("Lazy load options")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut lazy_load_members = false;
        let mut include_redundant_members = false;
        while let Some((key, value)) = access.next_entry::<String, bool>()? {
            match &*key {
                "lazy_load_members" => lazy_load_members = value,
                "include_redundant_members" => include_redundant_members = value,
                _ => {}
            };
        }

        Ok(if lazy_load_members {
            LazyLoadOptions::Enabled { include_redundant_members }
        } else {
            LazyLoadOptions::Disabled
        })
    }
}

impl<'de> Deserialize<'de> for LazyLoadOptions {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(LazyLoadOptionsVisitor)
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::LazyLoadOptions;

    #[test]
    fn test_serializing_disabled_lazy_load() {
        let lazy_load_options = LazyLoadOptions::Disabled;
        assert_eq!(to_json_value(lazy_load_options).unwrap(), json!({}));
    }

    #[test]
    fn test_serializing_lazy_load_no_redundant() {
        let lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: false };
        assert_eq!(to_json_value(lazy_load_options).unwrap(), json!({ "lazy_load_members": true }));
    }

    #[test]
    fn test_serializing_lazy_load_with_redundant() {
        let lazy_load_options = LazyLoadOptions::Enabled { include_redundant_members: true };
        assert_eq!(
            to_json_value(lazy_load_options).unwrap(),
            json!({ "lazy_load_members": true, "include_redundant_members": true })
        );
    }

    #[test]
    fn test_deserializing_no_lazy_load() {
        let json = json!({});
        assert_eq!(from_json_value::<LazyLoadOptions>(json).unwrap(), LazyLoadOptions::Disabled,);
    }

    #[test]
    fn test_deserializing_ignore_redundant_members_when_no_lazy_load() {
        let json = json!({ "include_redundant_members": true });
        assert_eq!(from_json_value::<LazyLoadOptions>(json).unwrap(), LazyLoadOptions::Disabled,);
    }
}
<|endoftext|>


<commit_before>// -*- rust -*-

use std;
import std::comm;
import std::comm::mk_port;

// Tests of ports and channels on various types
fn test_rec() {
    type r = {val0: int, val1: u8, val2: char};

    let po = comm::mk_port();
    let ch = po.mk_chan();
    let r0: r = {val0: 0, val1: 1u8, val2: '2'};
    ch.send(r0);
    let r1: r;
    r1 = po.recv();
    assert (r1.val0 == 0);
    assert (r1.val1 == 1u8);
    assert (r1.val2 == '2');
}

fn test_vec() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let v0: int[] = ~[0, 1, 2];
    ch.send(v0);
    let v1: int[];
    v1 = po.recv();
    assert (v1.(0) == 0);
    assert (v1.(1) == 1);
    assert (v1.(2) == 2);
}

fn test_str() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let s0: str = "test";
    ch.send(s0);
    let s1: str;
    s1 = po.recv();
    assert (s1.(0) as u8 == 't' as u8);
    assert (s1.(1) as u8 == 'e' as u8);
    assert (s1.(2) as u8 == 's' as u8);
    assert (s1.(3) as u8 == 't' as u8);
}

fn test_tag() {
    tag t { tag1; tag2(int); tag3(int, u8, char); }
    let po = comm::mk_port();
    let ch = po.mk_chan();
    ch.send(tag1);
    ch.send(tag2(10));
    ch.send(tag3(10, 11u8, 'A'));
    // FIXME: Do port semantics really guarantee these happen in order?
    let t1: t;
    t1 = po.recv();
    assert (t1 == tag1);
    t1 = po.recv();
    assert (t1 == tag2(10));
    t1 = po.recv();
    assert (t1 == tag3(10, 11u8, 'A'));
}

fn test_chan() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let po0 = comm::mk_port();
    let ch0 = po0.mk_chan();
    ch0.unsafe_ptr(); // bump ref count to prevent valgrind errors.
    ch.send(ch0);
    let ch1;
    ch1 = po.recv();
    // Does the transmitted channel still work?

    ch1.send(10);
    let i: int;
    i = po0.recv();
    assert (i == 10);
}

fn main() {
    test_rec();
    test_vec();
    test_str();
    test_tag();
    //test_chan();
}<commit_msg>Using unsafe chan pointer idiom to make task-comm-16.rs pass again.<commit_after>// -*- rust -*-

use std;
import std::comm;
import std::comm::mk_port;

// Tests of ports and channels on various types
fn test_rec() {
    type r = {val0: int, val1: u8, val2: char};

    let po = comm::mk_port();
    let ch = po.mk_chan();
    let r0: r = {val0: 0, val1: 1u8, val2: '2'};
    ch.send(r0);
    let r1: r;
    r1 = po.recv();
    assert (r1.val0 == 0);
    assert (r1.val1 == 1u8);
    assert (r1.val2 == '2');
}

fn test_vec() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let v0: int[] = ~[0, 1, 2];
    ch.send(v0);
    let v1: int[];
    v1 = po.recv();
    assert (v1.(0) == 0);
    assert (v1.(1) == 1);
    assert (v1.(2) == 2);
}

fn test_str() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let s0: str = "test";
    ch.send(s0);
    let s1: str;
    s1 = po.recv();
    assert (s1.(0) as u8 == 't' as u8);
    assert (s1.(1) as u8 == 'e' as u8);
    assert (s1.(2) as u8 == 's' as u8);
    assert (s1.(3) as u8 == 't' as u8);
}

fn test_tag() {
    tag t { tag1; tag2(int); tag3(int, u8, char); }
    let po = comm::mk_port();
    let ch = po.mk_chan();
    ch.send(tag1);
    ch.send(tag2(10));
    ch.send(tag3(10, 11u8, 'A'));
    // FIXME: Do port semantics really guarantee these happen in order?
    let t1: t;
    t1 = po.recv();
    assert (t1 == tag1);
    t1 = po.recv();
    assert (t1 == tag2(10));
    t1 = po.recv();
    assert (t1 == tag3(10, 11u8, 'A'));
}

fn test_chan() {
    let po = comm::mk_port();
    let ch = po.mk_chan();
    let po0 = comm::mk_port();
    let ch0 = po0.mk_chan();
    ch.send(ch0.unsafe_ptr());
    let pch1;
    pch1 = po.recv();
    let ch1 = comm::chan_from_unsafe_ptr(pch1);
    // Does the transmitted channel still work?

    ch1.send(10);
    let i: int;
    i = po0.recv();
    assert (i == 10);
}

fn main() {
    test_rec();
    test_vec();
    test_str();
    test_tag();
    test_chan();
}<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use anyhow::{bail, Context};
use serde::Deserialize;
use std::{
    env, fs,
    path::{Path, PathBuf},
    process::Command,
};
use tempfile::NamedTempFile;

#[derive(Deserialize)]
pub struct Metadata {
    pub target_directory: PathBuf,
    pub workspace_root: PathBuf,
}

pub fn metadata() -> Result<Metadata> {
    let output = Command::new("cargo")
        .arg("metadata")
        .arg("--no-deps")
        .arg("--format-version=1")
        .output()
        .context("Failed to query cargo metadata")?;

    serde_json::from_slice(&output.stdout).map_err(Into::into)
}

/// Get the diem node binary from the current working directory
pub fn get_diem_node_binary_from_worktree() -> Result<(String, PathBuf)> {
    let metadata = metadata()?;
    let mut revision = git_rev_parse(&metadata, "HEAD")?;
    if git_is_worktree_dirty()? {
        revision.push_str("-dirty");
    }

    let bin_path = cargo_build_diem_node(&metadata.workspace_root, &metadata.target_directory)?;

    Ok((revision, bin_path))
}

/// This function will attempt to build the diem-node binary at an arbitrary revision.
/// Using the `target/forge` as a working directory it will do the following:
///     1. Look for a binary named `diem-node--<revision>`, if it already exists return it
///     2. If the binary doesn't exist check out the revision to `target/forge/revision` by doing
///        `git archive --format=tar <revision> | tar x`
///     3. Using the `target/forge/target` directory as a cargo artifact directory, build the
///        binary and then move it to `target/forge/diem-node--<revision>`
pub fn get_diem_node_binary_at_revision(revision: &str) -> Result<(String, PathBuf)> {
    let metadata = metadata()?;
    let forge_directory = metadata.target_directory.join("forge");
    let revision = git_rev_parse(&metadata, format!("{}^{{commit}}", revision))?;
    let checkout_dir = forge_directory.join(&revision);
    let forge_target_directory = forge_directory.join("target");
    let diem_node_bin = forge_directory.join(format!(
        "diem-node--{}{}",
        revision,
        env::consts::EXE_SUFFIX
    ));

    if diem_node_bin.exists() {
        return Ok((revision, diem_node_bin));
    }

    fs::create_dir_all(&forge_target_directory)?;

    checkout_revision(&metadata, &revision, &checkout_dir)?;

    fs::rename(
        cargo_build_diem_node(&checkout_dir, &forge_target_directory)?,
        &diem_node_bin,
    )?;

    let _ = fs::remove_dir_all(&checkout_dir);

    Ok((revision, diem_node_bin))
}

fn git_rev_parse<R: AsRef<str>>(metadata: &Metadata, rev: R) -> Result<String> {
    let rev = rev.as_ref();
    let output = Command::new("git")
        .current_dir(&metadata.workspace_root)
        .arg("rev-parse")
        .arg(rev)
        .output()
        .context("Failed to parse revision")?;
    if output.status.success() {
        String::from_utf8(output.stdout)
            .map(|s| s.trim().to_owned())
            .map_err(Into::into)
    } else {
        bail!("Failed to parse revision: {}", rev);
    }
}

// Determine if the worktree is dirty
fn git_is_worktree_dirty() -> Result<bool> {
    Command::new("git")
        .args(&["diff-index", "--name-only", "HEAD", "--"])
        .output()
        .context("Failed to determine if the worktree is dirty")
        .map(|output| !output.stdout.is_empty())
}

/// Attempt to query the local git repository's remotes for the one that points to the upstream
/// diem/diem repository, falling back to "origin" if unable to locate the remote
pub fn git_get_upstream_remote() -> Result<String> {
    let output = Command::new("sh")
        .arg("-c")
        .arg("git remote -v | grep \"https://github.com/diem/diem.* (fetch)\" | cut -f1")
        .output()
        .context("Failed to get upstream remote")?;

    if output.status.success() {
        let remote = String::from_utf8(output.stdout).map(|s| s.trim().to_owned())?;

        // If its empty, fall back to "origin"
        if remote.is_empty() {
            Ok("origin".into())
        } else {
            Ok(remote)
        }
    } else {
        Ok("origin".into())
    }
}

pub fn git_merge_base<R: AsRef<str>>(rev: R) -> Result<String> {
    let rev = rev.as_ref();
    let output = Command::new("git")
        .arg("merge-base")
        .arg("HEAD")
        .arg(rev)
        .output()
        .context("Failed to find merge base")?;
    if output.status.success() {
        String::from_utf8(output.stdout)
            .map(|s| s.trim().to_owned())
            .map_err(Into::into)
    } else {
        bail!("Failed to find merge base between: {} and HEAD", rev);
    }
}

fn cargo_build_diem_node<D, T>(directory: D, target_directory: T) -> Result<PathBuf>
where
    D: AsRef<Path>,
    T: AsRef<Path>,
{
    let target_directory = target_directory.as_ref();
    let output = Command::new("cargo")
        .current_dir(directory)
        .env("CARGO_TARGET_DIR", target_directory)
        .args(&["build", "--bin=diem-node"])
        .output()
        .context("Failed to build diem-node")?;

    if output.status.success() {
        let bin_path =
            target_directory.join(format!("debug/{}{}", "diem-node", env::consts::EXE_SUFFIX));
        if !bin_path.exists() {
            bail!(
                "Can't find binary diem-node at expected path {:?}",
                bin_path
            );
        }

        Ok(bin_path)
    } else {
        bail!("Faild to build diem-node");
    }
}

fn checkout_revision(metadata: &Metadata, revision: &str, to: &Path) -> Result<()> {
    fs::create_dir_all(to)?;

    let archive_file = NamedTempFile::new()?.into_temp_path();

    let output = Command::new("git")
        .current_dir(&metadata.workspace_root)
        .arg("archive")
        .arg("--format=tar")
        .arg("--output")
        .arg(&archive_file)
        .arg(&revision)
        .output()
        .context("Failed to run git archive")?;
    if !output.status.success() {
        bail!("Failed to run git archive");
    }

    let output = Command::new("tar")
        .current_dir(to)
        .arg("xf")
        .arg(&archive_file)
        .output()
        .context("Failed to run tar")?;

    if !output.status.success() {
        bail!("Failed to run tar");
    }

    Ok(())
}
<commit_msg>forge: print out build errors when diem-node fails to build<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use anyhow::{bail, Context};
use serde::Deserialize;
use std::{
    env, fs,
    io::{self, Write},
    path::{Path, PathBuf},
    process::Command,
};
use tempfile::NamedTempFile;

#[derive(Deserialize)]
pub struct Metadata {
    pub target_directory: PathBuf,
    pub workspace_root: PathBuf,
}

pub fn metadata() -> Result<Metadata> {
    let output = Command::new("cargo")
        .arg("metadata")
        .arg("--no-deps")
        .arg("--format-version=1")
        .output()
        .context("Failed to query cargo metadata")?;

    serde_json::from_slice(&output.stdout).map_err(Into::into)
}

/// Get the diem node binary from the current working directory
pub fn get_diem_node_binary_from_worktree() -> Result<(String, PathBuf)> {
    let metadata = metadata()?;
    let mut revision = git_rev_parse(&metadata, "HEAD")?;
    if git_is_worktree_dirty()? {
        revision.push_str("-dirty");
    }

    let bin_path = cargo_build_diem_node(&metadata.workspace_root, &metadata.target_directory)?;

    Ok((revision, bin_path))
}

/// This function will attempt to build the diem-node binary at an arbitrary revision.
/// Using the `target/forge` as a working directory it will do the following:
///     1. Look for a binary named `diem-node--<revision>`, if it already exists return it
///     2. If the binary doesn't exist check out the revision to `target/forge/revision` by doing
///        `git archive --format=tar <revision> | tar x`
///     3. Using the `target/forge/target` directory as a cargo artifact directory, build the
///        binary and then move it to `target/forge/diem-node--<revision>`
pub fn get_diem_node_binary_at_revision(revision: &str) -> Result<(String, PathBuf)> {
    let metadata = metadata()?;
    let forge_directory = metadata.target_directory.join("forge");
    let revision = git_rev_parse(&metadata, format!("{}^{{commit}}", revision))?;
    let checkout_dir = forge_directory.join(&revision);
    let forge_target_directory = forge_directory.join("target");
    let diem_node_bin = forge_directory.join(format!(
        "diem-node--{}{}",
        revision,
        env::consts::EXE_SUFFIX
    ));

    if diem_node_bin.exists() {
        return Ok((revision, diem_node_bin));
    }

    fs::create_dir_all(&forge_target_directory)?;

    checkout_revision(&metadata, &revision, &checkout_dir)?;

    fs::rename(
        cargo_build_diem_node(&checkout_dir, &forge_target_directory)?,
        &diem_node_bin,
    )?;

    let _ = fs::remove_dir_all(&checkout_dir);

    Ok((revision, diem_node_bin))
}

fn git_rev_parse<R: AsRef<str>>(metadata: &Metadata, rev: R) -> Result<String> {
    let rev = rev.as_ref();
    let output = Command::new("git")
        .current_dir(&metadata.workspace_root)
        .arg("rev-parse")
        .arg(rev)
        .output()
        .context("Failed to parse revision")?;
    if output.status.success() {
        String::from_utf8(output.stdout)
            .map(|s| s.trim().to_owned())
            .map_err(Into::into)
    } else {
        bail!("Failed to parse revision: {}", rev);
    }
}

// Determine if the worktree is dirty
fn git_is_worktree_dirty() -> Result<bool> {
    Command::new("git")
        .args(&["diff-index", "--name-only", "HEAD", "--"])
        .output()
        .context("Failed to determine if the worktree is dirty")
        .map(|output| !output.stdout.is_empty())
}

/// Attempt to query the local git repository's remotes for the one that points to the upstream
/// diem/diem repository, falling back to "origin" if unable to locate the remote
pub fn git_get_upstream_remote() -> Result<String> {
    let output = Command::new("sh")
        .arg("-c")
        .arg("git remote -v | grep \"https://github.com/diem/diem.* (fetch)\" | cut -f1")
        .output()
        .context("Failed to get upstream remote")?;

    if output.status.success() {
        let remote = String::from_utf8(output.stdout).map(|s| s.trim().to_owned())?;

        // If its empty, fall back to "origin"
        if remote.is_empty() {
            Ok("origin".into())
        } else {
            Ok(remote)
        }
    } else {
        Ok("origin".into())
    }
}

pub fn git_merge_base<R: AsRef<str>>(rev: R) -> Result<String> {
    let rev = rev.as_ref();
    let output = Command::new("git")
        .arg("merge-base")
        .arg("HEAD")
        .arg(rev)
        .output()
        .context("Failed to find merge base")?;
    if output.status.success() {
        String::from_utf8(output.stdout)
            .map(|s| s.trim().to_owned())
            .map_err(Into::into)
    } else {
        bail!("Failed to find merge base between: {} and HEAD", rev);
    }
}

fn cargo_build_diem_node<D, T>(directory: D, target_directory: T) -> Result<PathBuf>
where
    D: AsRef<Path>,
    T: AsRef<Path>,
{
    let target_directory = target_directory.as_ref();
    let directory = directory.as_ref();
    let output = Command::new("cargo")
        .current_dir(directory)
        .env("CARGO_TARGET_DIR", target_directory)
        .args(&["build", "--bin=diem-node"])
        .output()
        .context("Failed to build diem-node")?;

    if output.status.success() {
        let bin_path =
            target_directory.join(format!("debug/{}{}", "diem-node", env::consts::EXE_SUFFIX));
        if !bin_path.exists() {
            bail!(
                "Can't find binary diem-node at expected path {:?}",
                bin_path
            );
        }

        Ok(bin_path)
    } else {
        io::stderr().write_all(&output.stderr)?;

        bail!(
            "Failed to build diem-node: 'cd {} && CARGO_TARGET_DIR={} cargo build --bin=diem-node",
            directory.display(),
            target_directory.display(),
        );
    }
}

fn checkout_revision(metadata: &Metadata, revision: &str, to: &Path) -> Result<()> {
    fs::create_dir_all(to)?;

    let archive_file = NamedTempFile::new()?.into_temp_path();

    let output = Command::new("git")
        .current_dir(&metadata.workspace_root)
        .arg("archive")
        .arg("--format=tar")
        .arg("--output")
        .arg(&archive_file)
        .arg(&revision)
        .output()
        .context("Failed to run git archive")?;
    if !output.status.success() {
        bail!("Failed to run git archive");
    }

    let output = Command::new("tar")
        .current_dir(to)
        .arg("xf")
        .arg(&archive_file)
        .output()
        .context("Failed to run tar")?;

    if !output.status.success() {
        bail!("Failed to run tar");
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license

use energy::{PairPotential, PairRestriction};
use types::{Matrix3, One, Vector3D, Zero};

/// The different way to compute non-bonded pair interactions
#[derive(Clone, Copy, Debug)]
enum PairComputation {
    /// Using only a cutoff distance
    Cutoff,
    /// Using a cutoff distance and a shift
    Shifted(f64),
}

/// A non-bonded interaction between two particle.
///
/// This is a thin wrapper around a [`Box<PairPotential>`][PairPotential]
/// associated with a [pair restriction][PairRestriction]. It ensure that the
/// potential is computed up to a cutoff distance. An additional shifting of the
/// potential can be used in molecular dynamics, to ensure that the energy is
/// continuous at the cutoff distance.
///
/// [PairPotential]: trait.PairPotential.html
/// [PairRestriction]: enum.PairRestriction.html
#[derive(Clone)]
pub struct PairInteraction {
    /// The potential of this interaction
    potential: Box<PairPotential>,
    /// The associated pair restrictions
    restriction: PairRestriction,
    /// The cutoff distance
    cutoff: f64,
    /// Should we use tail corrections
    tail: bool,
    /// The computation mode
    computation: PairComputation,
}

impl PairInteraction {
    /// Create a new `PairInteraction` for the given `potential` and using the
    /// given `cutoff` distance.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), 0.525);
    /// // energy at and after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn new(potential: Box<PairPotential>, cutoff: f64) -> PairInteraction {
        PairInteraction {
            potential: potential,
            cutoff: cutoff,
            restriction: PairRestriction::None,
            computation: PairComputation::Cutoff,
            tail: false,
        }
    }

    /// Create a new `PairInteraction` with the given `cutoff`, using shifted
    /// computation of the energy.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::shifted(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), -4.2);
    ///
    /// // energy goes smoothly to zero at the cutoff
    /// assert!(interaction.energy(1.999).abs() < 0.01);
    /// // energy after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn shifted(potential: Box<PairPotential>, cutoff: f64) -> PairInteraction {
        let shift = potential.energy(cutoff);
        PairInteraction {
            potential: potential,
            cutoff: cutoff,
            restriction: PairRestriction::None,
            computation: PairComputation::Shifted(shift),
            tail: false,
        }
    }

    /// Enable the use of tail corrections for energy and virial contribution
    /// of this pair interaction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.tail_energy(), 0.0);
    ///
    /// interaction.enable_tail_corrections();
    /// assert!(interaction.tail_energy() < 0.0);
    /// ```
    pub fn enable_tail_corrections(&mut self) {
        self.tail = true;
    }

    /// Get the associated pair restriction. The default is to have no pair
    /// restriction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::{NullPotential, PairRestriction};
    /// let interaction = PairInteraction::new(Box::new(NullPotential), 2.0);
    ///
    /// assert_eq!(interaction.restriction(), PairRestriction::None);
    /// ```
    pub fn restriction(&self) -> PairRestriction {
        self.restriction
    }

    /// Set the pair restriction associated with this interaction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::{NullPotential, PairRestriction};
    /// let mut interaction = PairInteraction::new(Box::new(NullPotential), 2.0);
    ///
    /// assert_eq!(interaction.restriction(), PairRestriction::None);
    /// interaction.set_restriction(PairRestriction::Exclude12);
    /// assert_eq!(interaction.restriction(), PairRestriction::Exclude12);
    /// ```
    pub fn set_restriction(&mut self, restriction: PairRestriction) {
        self.restriction = restriction;
    }

    /// Return the cutoff radius
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let ar = LennardJones{sigma: 3.405, epsilon: 1.0};
    /// let interaction = PairInteraction::new(Box::new(ar), 9.1935);
    /// let rc = interaction.cutoff();
    /// assert_eq!(rc, 2.7f64 * 3.405);
    /// ```
    pub fn cutoff(&self) -> f64 {
        self.cutoff
    }
}

impl PairInteraction {
    /// Get the energy for this pair interaction at the distance `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), 0.525);
    /// // energy at and after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn energy(&self, r: f64) -> f64 {
        if r >= self.cutoff {
            0.0
        } else {
            let energy = self.potential.energy(r);
            match self.computation {
                PairComputation::Cutoff => energy,
                PairComputation::Shifted(shift) => energy - shift,
            }
        }
    }

    /// Get the norm of the force for this pair interaction at the distance `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.force(1.0), -2.1);
    /// // force at and after the cutoff is zero
    /// assert_eq!(interaction.force(2.0), 0.0);
    /// ```
    pub fn force(&self, r: f64) -> f64 {
        if r >= self.cutoff {
            0.0
        } else {
            self.potential.force(r)
        }
    }

    /// Get the virial contribution for this pair interaction at the distance
    /// `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    /// # use lumol_core::types::Vector3D;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::shifted(potential, 2.0);
    ///
    /// let r = Vector3D::new(1.0, 0.0, 0.3);
    /// let force = interaction.force(r.norm()) * r / r.norm();
    /// assert_eq!(interaction.virial(&r), r.tensorial(&force));
    /// ```
    pub fn virial(&self, r: &Vector3D) -> Matrix3 {
        if r.norm() >= self.cutoff {
            Matrix3::zero()
        } else {
            self.potential.virial(r)
        }
    }

    /// Get the tail correction to the energy for this pair interaction
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    /// interaction.enable_tail_corrections();
    ///
    /// assert_eq!(interaction.tail_energy(), -0.010936609903971353);
    /// ```
    pub fn tail_energy(&self) -> f64 {
        if self.tail {
            self.potential.tail_energy(self.cutoff)
        } else {
            0.0
        }
    }

    /// Get the tail correction to the virial for this pair interaction
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    /// # use lumol_core::types::Matrix3;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    /// interaction.enable_tail_corrections();
    ///
    /// let w = -0.02187143961588542;
    /// let virial = Matrix3::new([
    ///     [w, 0.0, 0.0],
    ///     [0.0, w, 0.0],
    ///     [0.0, 0.0, w],
    /// ]);
    ///
    /// assert_eq!(interaction.tail_virial(), virial);
    /// ```
    pub fn tail_virial(&self) -> Matrix3 {
        if self.tail {
            let tensor = Matrix3::one() / 3.0;
            return self.potential.tail_virial(self.cutoff) * tensor;
        } else {
            return Matrix3::zero();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use energy::{LennardJones, NullPotential, PairRestriction};
    use energy::Potential;

    #[test]
    fn restriction() {
        let mut pairs = PairInteraction::new(Box::new(NullPotential), 10.0);
        assert_eq!(pairs.restriction(), PairRestriction::None);

        pairs.set_restriction(PairRestriction::Exclude13);
        assert_eq!(pairs.restriction(), PairRestriction::Exclude13);
    }

    #[test]
    fn test_cutoff() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let pairs = PairInteraction::new(Box::new(lj), 4.0);

        assert_eq!(pairs.force(2.5), lj.force(2.5));
        assert_eq!(pairs.energy(2.5), lj.energy(2.5));

        assert_eq!(pairs.force(4.1), 0.0);
        assert_eq!(pairs.energy(4.1), 0.0);

        assert_eq!(pairs.cutoff(), 4.0);
    }

    #[test]
    fn shifted() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let pairs = PairInteraction::shifted(Box::new(lj), 4.0);

        assert_eq!(pairs.force(2.5), lj.force(2.5));
        assert_ulps_eq!(pairs.energy(2.5), -0.030681134109158216);

        assert_eq!(pairs.force(4.1), 0.0);
        assert_eq!(pairs.energy(4.1), 0.0);
    }

    #[test]
    fn tail_corrections() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let mut pairs = PairInteraction::shifted(Box::new(lj), 4.0);
        pairs.enable_tail_corrections();

        assert_eq!(pairs.tail_energy(), -0.041663275824652776);
        assert_ulps_eq!(pairs.tail_virial().trace(), -0.24995930989583334);
    }
}
<commit_msg>USe ulps equality for shifted calculations<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license

use energy::{PairPotential, PairRestriction};
use types::{Matrix3, One, Vector3D, Zero};

/// The different way to compute non-bonded pair interactions
#[derive(Clone, Copy, Debug)]
enum PairComputation {
    /// Using only a cutoff distance
    Cutoff,
    /// Using a cutoff distance and a shift
    Shifted(f64),
}

/// A non-bonded interaction between two particle.
///
/// This is a thin wrapper around a [`Box<PairPotential>`][PairPotential]
/// associated with a [pair restriction][PairRestriction]. It ensure that the
/// potential is computed up to a cutoff distance. An additional shifting of the
/// potential can be used in molecular dynamics, to ensure that the energy is
/// continuous at the cutoff distance.
///
/// [PairPotential]: trait.PairPotential.html
/// [PairRestriction]: enum.PairRestriction.html
#[derive(Clone)]
pub struct PairInteraction {
    /// The potential of this interaction
    potential: Box<PairPotential>,
    /// The associated pair restrictions
    restriction: PairRestriction,
    /// The cutoff distance
    cutoff: f64,
    /// Should we use tail corrections
    tail: bool,
    /// The computation mode
    computation: PairComputation,
}

impl PairInteraction {
    /// Create a new `PairInteraction` for the given `potential` and using the
    /// given `cutoff` distance.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), 0.525);
    /// // energy at and after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn new(potential: Box<PairPotential>, cutoff: f64) -> PairInteraction {
        PairInteraction {
            potential: potential,
            cutoff: cutoff,
            restriction: PairRestriction::None,
            computation: PairComputation::Cutoff,
            tail: false,
        }
    }

    /// Create a new `PairInteraction` with the given `cutoff`, using shifted
    /// computation of the energy.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::shifted(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), -4.2);
    ///
    /// // energy goes smoothly to zero at the cutoff
    /// assert!(interaction.energy(1.999).abs() < 0.01);
    /// // energy after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn shifted(potential: Box<PairPotential>, cutoff: f64) -> PairInteraction {
        let shift = potential.energy(cutoff);
        PairInteraction {
            potential: potential,
            cutoff: cutoff,
            restriction: PairRestriction::None,
            computation: PairComputation::Shifted(shift),
            tail: false,
        }
    }

    /// Enable the use of tail corrections for energy and virial contribution
    /// of this pair interaction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.tail_energy(), 0.0);
    ///
    /// interaction.enable_tail_corrections();
    /// assert!(interaction.tail_energy() < 0.0);
    /// ```
    pub fn enable_tail_corrections(&mut self) {
        self.tail = true;
    }

    /// Get the associated pair restriction. The default is to have no pair
    /// restriction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::{NullPotential, PairRestriction};
    /// let interaction = PairInteraction::new(Box::new(NullPotential), 2.0);
    ///
    /// assert_eq!(interaction.restriction(), PairRestriction::None);
    /// ```
    pub fn restriction(&self) -> PairRestriction {
        self.restriction
    }

    /// Set the pair restriction associated with this interaction.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::{NullPotential, PairRestriction};
    /// let mut interaction = PairInteraction::new(Box::new(NullPotential), 2.0);
    ///
    /// assert_eq!(interaction.restriction(), PairRestriction::None);
    /// interaction.set_restriction(PairRestriction::Exclude12);
    /// assert_eq!(interaction.restriction(), PairRestriction::Exclude12);
    /// ```
    pub fn set_restriction(&mut self, restriction: PairRestriction) {
        self.restriction = restriction;
    }

    /// Return the cutoff radius
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let ar = LennardJones{sigma: 3.405, epsilon: 1.0};
    /// let interaction = PairInteraction::new(Box::new(ar), 9.1935);
    /// let rc = interaction.cutoff();
    /// assert_eq!(rc, 2.7f64 * 3.405);
    /// ```
    pub fn cutoff(&self) -> f64 {
        self.cutoff
    }
}

impl PairInteraction {
    /// Get the energy for this pair interaction at the distance `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.energy(1.0), 0.525);
    /// // energy at and after the cutoff is zero
    /// assert_eq!(interaction.energy(2.0), 0.0);
    /// ```
    pub fn energy(&self, r: f64) -> f64 {
        if r >= self.cutoff {
            0.0
        } else {
            let energy = self.potential.energy(r);
            match self.computation {
                PairComputation::Cutoff => energy,
                PairComputation::Shifted(shift) => energy - shift,
            }
        }
    }

    /// Get the norm of the force for this pair interaction at the distance `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::new(potential, 2.0);
    ///
    /// assert_eq!(interaction.force(1.0), -2.1);
    /// // force at and after the cutoff is zero
    /// assert_eq!(interaction.force(2.0), 0.0);
    /// ```
    pub fn force(&self, r: f64) -> f64 {
        if r >= self.cutoff {
            0.0
        } else {
            self.potential.force(r)
        }
    }

    /// Get the virial contribution for this pair interaction at the distance
    /// `r`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::Harmonic;
    /// # use lumol_core::types::Vector3D;
    ///
    /// let potential = Box::new(Harmonic{x0: 0.5, k: 4.2});
    /// let interaction = PairInteraction::shifted(potential, 2.0);
    ///
    /// let r = Vector3D::new(1.0, 0.0, 0.3);
    /// let force = interaction.force(r.norm()) * r / r.norm();
    /// assert_eq!(interaction.virial(&r), r.tensorial(&force));
    /// ```
    pub fn virial(&self, r: &Vector3D) -> Matrix3 {
        if r.norm() >= self.cutoff {
            Matrix3::zero()
        } else {
            self.potential.virial(r)
        }
    }

    /// Get the tail correction to the energy for this pair interaction
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    /// interaction.enable_tail_corrections();
    ///
    /// assert_eq!(interaction.tail_energy(), -0.010936609903971353);
    /// ```
    pub fn tail_energy(&self) -> f64 {
        if self.tail {
            self.potential.tail_energy(self.cutoff)
        } else {
            0.0
        }
    }

    /// Get the tail correction to the virial for this pair interaction
    ///
    /// # Examples
    ///
    /// ```
    /// use lumol_core::energy::PairInteraction;
    /// use lumol_core::energy::LennardJones;
    /// # use lumol_core::types::Matrix3;
    ///
    /// let potential = Box::new(LennardJones{sigma: 0.5, epsilon: 4.2});
    /// let mut interaction = PairInteraction::new(potential, 2.0);
    /// interaction.enable_tail_corrections();
    ///
    /// let w = -0.02187143961588542;
    /// let virial = Matrix3::new([
    ///     [w, 0.0, 0.0],
    ///     [0.0, w, 0.0],
    ///     [0.0, 0.0, w],
    /// ]);
    ///
    /// assert_eq!(interaction.tail_virial(), virial);
    /// ```
    pub fn tail_virial(&self) -> Matrix3 {
        if self.tail {
            let tensor = Matrix3::one() / 3.0;
            return self.potential.tail_virial(self.cutoff) * tensor;
        } else {
            return Matrix3::zero();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use energy::{LennardJones, NullPotential, PairRestriction};
    use energy::Potential;

    #[test]
    fn restriction() {
        let mut pairs = PairInteraction::new(Box::new(NullPotential), 10.0);
        assert_eq!(pairs.restriction(), PairRestriction::None);

        pairs.set_restriction(PairRestriction::Exclude13);
        assert_eq!(pairs.restriction(), PairRestriction::Exclude13);
    }

    #[test]
    fn test_cutoff() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let pairs = PairInteraction::new(Box::new(lj), 4.0);

        assert_eq!(pairs.force(2.5), lj.force(2.5));
        assert_eq!(pairs.energy(2.5), lj.energy(2.5));

        assert_eq!(pairs.force(4.1), 0.0);
        assert_eq!(pairs.energy(4.1), 0.0);

        assert_eq!(pairs.cutoff(), 4.0);
    }

    #[test]
    fn shifted() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let pairs = PairInteraction::shifted(Box::new(lj), 4.0);

        assert_ulps_eq!(pairs.force(2.5), lj.force(2.5));
        assert_ulps_eq!(pairs.energy(2.5), -0.030681134109158216);

        assert_eq!(pairs.force(4.1), 0.0);
        assert_eq!(pairs.energy(4.1), 0.0);
    }

    #[test]
    fn tail_corrections() {
        let lj = LennardJones {
            sigma: 1.0,
            epsilon: 2.0,
        };
        let mut pairs = PairInteraction::shifted(Box::new(lj), 4.0);
        pairs.enable_tail_corrections();

        assert_eq!(pairs.tail_energy(), -0.041663275824652776);
        assert_ulps_eq!(pairs.tail_virial().trace(), -0.24995930989583334);
    }
}
<|endoftext|>


<commit_before>use crate::utils::span_lint;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use std::f64::consts as f64;
use syntax::ast::{FloatTy, LitFloatType, LitKind};
use syntax::symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for floating point literals that approximate
    /// constants which are defined in
    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
    /// or
    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
    /// respectively, suggesting to use the predefined constant.
    ///
    /// **Why is this bad?** Usually, the definition in the standard library is more
    /// precise than what people come up with. If you find that your definition is
    /// actually more precise, please [file a Rust
    /// issue](https://github.com/rust-lang/rust/issues).
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let x = 3.14;
    /// ```
    pub APPROX_CONSTANT,
    correctness,
    "the approximate of a known float constant (in `std::fXX::consts`)"
}

// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: [(f64, &str, usize); 16] = [
    (f64::E, "E", 4),
    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
    (f64::LN_10, "LN_10", 5),
    (f64::LN_2, "LN_2", 5),
    (f64::LOG10_E, "LOG10_E", 5),
    (f64::LOG2_E, "LOG2_E", 5),
    (f64::PI, "PI", 3),
    (f64::SQRT_2, "SQRT_2", 5),
];

declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Lit(lit) = &e.kind {
            check_lit(cx, &lit.node, e);
        }
    }
}

fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr) {
    match *lit {
        LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
            FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
            FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
        },
        LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
        _ => (),
    }
}

fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, module: &str) {
    let s = s.as_str();
    if s.parse::<f64>().is_ok() {
        for &(constant, name, min_digits) in &KNOWN_CONSTS {
            if is_approx_const(constant, &s, min_digits) {
                span_lint(
                    cx,
                    APPROX_CONSTANT,
                    e.span,
                    &format!(
                        "approximate value of `{}::consts::{}` found. \
                         Consider using it directly",
                        module, &name
                    ),
                );
                return;
            }
        }
    }
}

/// Returns `false` if the number of significant figures in `value` are
/// less than `min_digits`; otherwise, returns true if `value` is equal
/// to `constant`, rounded to the number of digits present in `value`.
#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
    if value.len() <= min_digits {
        false
    } else if constant.to_string().starts_with(value) {
        // The value is a truncated constant
        true
    } else {
        let round_const = format!("{:.*}", value.len() - 2, constant);
        value == round_const
    }
}
<commit_msg>add a good example for the approx_const lint<commit_after>use crate::utils::span_lint;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use std::f64::consts as f64;
use syntax::ast::{FloatTy, LitFloatType, LitKind};
use syntax::symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for floating point literals that approximate
    /// constants which are defined in
    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
    /// or
    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
    /// respectively, suggesting to use the predefined constant.
    ///
    /// **Why is this bad?** Usually, the definition in the standard library is more
    /// precise than what people come up with. If you find that your definition is
    /// actually more precise, please [file a Rust
    /// issue](https://github.com/rust-lang/rust/issues).
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// // Bad
    /// let x = 3.14;
    /// let y = 1_f64 / x;
    ///
    /// // Good
    /// let x = std::f32::consts::PI;
    /// let y = std::f64::consts::FRAC_1_PI;
    /// ```
    pub APPROX_CONSTANT,
    correctness,
    "the approximate of a known float constant (in `std::fXX::consts`)"
}

// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: [(f64, &str, usize); 16] = [
    (f64::E, "E", 4),
    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
    (f64::LN_10, "LN_10", 5),
    (f64::LN_2, "LN_2", 5),
    (f64::LOG10_E, "LOG10_E", 5),
    (f64::LOG2_E, "LOG2_E", 5),
    (f64::PI, "PI", 3),
    (f64::SQRT_2, "SQRT_2", 5),
];

declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Lit(lit) = &e.kind {
            check_lit(cx, &lit.node, e);
        }
    }
}

fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr) {
    match *lit {
        LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
            FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
            FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
        },
        LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
        _ => (),
    }
}

fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, module: &str) {
    let s = s.as_str();
    if s.parse::<f64>().is_ok() {
        for &(constant, name, min_digits) in &KNOWN_CONSTS {
            if is_approx_const(constant, &s, min_digits) {
                span_lint(
                    cx,
                    APPROX_CONSTANT,
                    e.span,
                    &format!(
                        "approximate value of `{}::consts::{}` found. \
                         Consider using it directly",
                        module, &name
                    ),
                );
                return;
            }
        }
    }
}

/// Returns `false` if the number of significant figures in `value` are
/// less than `min_digits`; otherwise, returns true if `value` is equal
/// to `constant`, rounded to the number of digits present in `value`.
#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
    if value.len() <= min_digits {
        false
    } else if constant.to_string().starts_with(value) {
        // The value is a truncated constant
        true
    } else {
        let round_const = format!("{:.*}", value.len() - 2, constant);
        value == round_const
    }
}
<|endoftext|>


<commit_before>// crypto_core_salsa20.h

pub const crypto_core_salsa20_OUTPUTBYTES: usize = 64;
pub const crypto_core_salsa20_INPUTBYTES: usize = 16;
pub const crypto_core_salsa20_KEYBYTES: usize = 32;
pub const crypto_core_salsa20_CONSTBYTES: usize = 16;

extern {
    pub fn crypto_core_salsa20_outputbytes() -> size_t;
    pub fn crypto_core_salsa20_inputbytes() -> size_t;
    pub fn crypto_core_salsa20_keybytes() -> size_t;
    pub fn crypto_core_salsa20_constbytes() -> size_t;

    pub fn crypto_core_salsa20(
        out: *mut [u8; crypto_core_salsa20_OUTPUTBYTES],
        in_: *const [u8; crypto_core_salsa20_INPUTBYTES],
        k: *const [u8; crypto_core_salsa20_KEYBYTES],
        c: *const [u8; crypto_core_salsa20_CONSTBYTES]) -> c_int;
}

#[test]
fn test_crypto_core_salsa20_outputbytes() {
    assert!(unsafe {
        crypto_core_salsa20_outputbytes() as usize
    } == crypto_core_salsa20_OUTPUTBYTES)
}
#[test]
fn test_crypto_core_salsa20_inputbytes() {
    assert!(unsafe {
        crypto_core_salsa20_inputbytes() as usize
    } == crypto_core_salsa20_INPUTBYTES)
}
#[test]
fn test_crypto_core_salsa20_keybytes() {
    assert!(unsafe {
        crypto_core_salsa20_keybytes() as usize
    } == crypto_core_salsa20_KEYBYTES)
}
#[test]
fn test_crypto_core_salsa20_constbytes() {
    assert!(unsafe {
        crypto_core_salsa20_constbytes() as usize
    } == crypto_core_salsa20_CONSTBYTES)
}
<commit_msg>whitespace changes<commit_after>// crypto_core_salsa20.h

pub const crypto_core_salsa20_OUTPUTBYTES: usize = 64;
pub const crypto_core_salsa20_INPUTBYTES: usize = 16;
pub const crypto_core_salsa20_KEYBYTES: usize = 32;
pub const crypto_core_salsa20_CONSTBYTES: usize = 16;

extern {
    pub fn crypto_core_salsa20_outputbytes() -> size_t;
    pub fn crypto_core_salsa20_inputbytes() -> size_t;
    pub fn crypto_core_salsa20_keybytes() -> size_t;
    pub fn crypto_core_salsa20_constbytes() -> size_t;

    pub fn crypto_core_salsa20(
        out: *mut [u8; crypto_core_salsa20_OUTPUTBYTES],
        in_: *const [u8; crypto_core_salsa20_INPUTBYTES],
        k: *const [u8; crypto_core_salsa20_KEYBYTES],
        c: *const [u8; crypto_core_salsa20_CONSTBYTES]) -> c_int;
}

#[test]
fn test_crypto_core_salsa20_outputbytes() {
    assert!(unsafe {
        crypto_core_salsa20_outputbytes() as usize
    } == crypto_core_salsa20_OUTPUTBYTES)
}

#[test]
fn test_crypto_core_salsa20_inputbytes() {
    assert!(unsafe {
        crypto_core_salsa20_inputbytes() as usize
    } == crypto_core_salsa20_INPUTBYTES)
}

#[test]
fn test_crypto_core_salsa20_keybytes() {
    assert!(unsafe {
        crypto_core_salsa20_keybytes() as usize
    } == crypto_core_salsa20_KEYBYTES)
}

#[test]
fn test_crypto_core_salsa20_constbytes() {
    assert!(unsafe {
        crypto_core_salsa20_constbytes() as usize
    } == crypto_core_salsa20_CONSTBYTES)
}
<|endoftext|>


<commit_before>use na::{Mat2, DMat};
//use na;
use std::num::*;
mod test;

#[test]
fn test_which_grid() {
    let loc = (-6., 6.);
    let i_rn = (-10.0, 10.0);
    let j_rn = (-10.0, 10.0);
    let i_bins = 10 as usize;
    let j_bins = 10 as usize;

    let result = grid_helpers::which_grid(loc, i_rn, j_rn, i_bins, j_bins).unwrap();
    assert_eq!((2, 8), result);

}

mod grid_helpers {
    pub fn which_grid(val: (f64, f64), i_range: (f64, f64), j_range: (f64, f64), i_bins: usize, j_bins: usize) -> Result<(usize, usize), String> {
        let i_size = (i_range.1 - i_range.0) / i_bins as f64;
        let i_number_of_sizes = ((val.0 - i_range.0) / i_size) as usize;
        let j_size = (j_range.1 - j_range.0) / j_bins as f64;
        let j_number_of_sizes = ((val.1 - j_range.0) / j_size) as usize;

        assert!(i_number_of_sizes < i_bins);
        assert!(j_number_of_sizes < j_bins);

        Ok((i_number_of_sizes, j_number_of_sizes))
    }
}

pub trait DensityGrid {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String>;

    fn compute_decay_coeff_at_time(&self, t_n: u32, t_l: u32, lambda: f64) -> f64 {
        lambda.powf((t_n - t_l) as f64)
    }
}

pub trait DensityGridSpace {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String>;

}

/*

input data: [(x, y, v)]

///some impl
for some time t in 0..:
  _.put t, [(x, y, v)]

//some other impl
fn put (t, [(x, y, v)]):
  [(i, j, sum_vals)] = [x,y,v].map(i,j,v).groupby(i,j).reduce(_+_)
  foreach:
    g = density_grids[i, j]
    g.update(t, v)
    print g.get()
*/
struct RawData;
struct TheWorld;
impl TheWorld {
    fn do_time_steps() {}
    fn do_one_time_step() {}
    fn put() {}
}
struct DG;
impl DG {
    fn update() {}
    fn get() {}
}

#[derive(Debug)]
#[derive(Copy)]
#[derive(Clone)]
pub struct GridPoint {
    i: usize, //location
    j: usize,
    density: f64,
    data_time: u32,
}

#[derive(Clone)]
pub struct Dat {
    x: f64,
    y: f64,
    val: f64,
    t: u32,
}

pub struct DStreamProps {
    c_m: f64,
    c_l: f64,
    lambda: f64,
    beta: f64,
    i_bins: usize,
    j_bins: usize,
    i_range: (f64, f64),
    j_range: (f64, f64),
}

pub struct TheGrid {
    mat: DMat<GridPoint>,
    props: DStreamProps,
    dats: Vec<((usize, usize), Dat)>,
}

impl Default for DStreamProps {
    fn default() -> DStreamProps {
        DStreamProps {
            c_m: 3.0,
            c_l: 0.8,
            lambda: 0.998,
            beta: 0.3,
            i_bins: 10 as usize,
            j_bins: 10 as usize,
            i_range: (-10.0, 10.0),
            j_range: (-10.0, 10.0),
        }
    }
}

impl DensityGrid for GridPoint {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String> {

        //TODO time - 1 is wrong, it should be biggest time smaller than current
        let coeff = self.compute_decay_coeff_at_time(time, time - 5, lambda);

        let sum_vals = dats.iter().fold(0.0, |sum, x| sum + x.1.val);
        println!("sum vals: {}", sum_vals);
        let new_d = coeff * sum_vals + 1.0;
        println!("new D: {}", new_d);

        Ok(new_d)
    }

//    fn update_with_values(&mut self, vals: &[f64], current_time: u32, lambda: f64) -> Result<(), String> {
//        //        D(g, t n ) = λ t n −t l D(g, t l ) + 1
//        let coeff = self.compute_decay_coeff_at_time(current_time, self.data_time, lambda);
//        println!("coeff: {}", coeff);
//        let sum_vals = vals.iter().fold(0.0, |sum, x| sum + x);
//        let new_d = coeff * coeff * sum_vals + 1.0;
//        println!("sum vals: {}", sum_vals);
//        println!("new D: {}", new_d);
//        self.density = new_d;
//        self.data_time = current_time;
//        Ok(())
//    }

}

impl DensityGridSpace for TheGrid {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String> {

//        println!("{}", self.density);
//        let props: DStreamProps = DStreamProps { ..Default::default() };
//        let idxs = grid_helpers::which_grid(val, props.i_range, props.j_range, props.i_bins, props.j_bins);
//        let res_update = self.update_grid_pt_density(val, 0, props.lambda);
//        println!("{}", self.density);

        if ((loc2d.0 <= self.props.i_range.1) &&
            (loc2d.0 >= self.props.i_range.0) &&
            (loc2d.1 <= self.props.j_range.1) &&
            (loc2d.1 >= self.props.j_range.0)) {

            println!("valid!");
            let idxs =
                grid_helpers::which_grid(loc2d, self.props.i_range, self.props.j_range,
                                         self.props.i_bins, self.props.j_bins).unwrap();
            let gp = &mut self.mat[idxs];

//            gp.update_with_values(&[100.0], t, self.props.lambda);

            self.dats.push((idxs, Dat {x: loc2d.0, y: loc2d.1, val: val, t:t}));

            println!("value at next time: {}",
                gp.get_value_at_time(
                    t+10,
                    self.props.lambda,
                        self.dats
                            .clone()
                            .into_iter()
                            .filter(|i| i.0 == idxs)
                            .collect::<Vec<((usize, usize), Dat)>>()).unwrap());

            println!("dats len: {}", self.dats.len());

            Ok(())
        } else {
            println!("invalid");
            Err(String::from("Invalid range"))
        }
    }
}
#[test]
fn test_put() {

    let mut grid_pt_1: GridPoint = GridPoint {i: 0, j: 0,
        density: 0.0, data_time: 0,};

    let mut grid_pt_2: GridPoint = GridPoint {i: 1, j: 0,
        density: 0.0, data_time: 0, };

    let mut grid_pt_3: GridPoint = GridPoint {i: 0, j: 1,
        density: 0.0, data_time: 0, };

    let mut grid_pt_4: GridPoint = GridPoint {i: 1, j: 1,
        density: 0.0, data_time: 0,};

    let mut v = Vec::new();
    for i in 0..100 {
        v.push(grid_pt_1)
    }

    let mut mat_thing = DMat::from_col_vec(
        10,
        10,
        &v
    );

    for i in 0..10 {

        for j in 0..10 {

            let mut g1= GridPoint
            {i: i, j: j,
                density: 0.0, data_time: 0, };

            mat_thing[(i,j)] = g1;
        }

    }

    let mut the_grid = TheGrid {mat: mat_thing, props: DStreamProps { ..Default::default() }, dats: Vec::new()};

    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 100).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1000).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10000).unwrap();

    for i in 0..10 {
        for j in 0..10 {
            let gp: &GridPoint = &the_grid.mat[(i as usize, j as usize)];
            assert_eq!(0.0, gp.density)
        }
    }
}

#[test]
fn test_initialize_clustering() {
    const loc_i: usize = 0 as usize;
    const loc_j: usize = 0 as usize;
    const G1: GridPoint = GridPoint
    {i: loc_i, j: loc_j,
        density: 0.0, data_time: 0, };
    let result = initialize_clustering(DMat::from_row_vec(2,2, &[G1, G1, G1, G1]));
    assert_eq!(Ok(()), result);
}

pub fn initialize_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update density of all grids in grid_list
    //assign each dense grid to a distinct cluster
    //label all other grids as NO_CLASS; bad grids!
    /*
    do until no change in cluster labels
          foreach cluster c
            foreach outside grid g of c
                foreach neighboring grid h of g
                    if h belongs to cluster c'
                        if |c| > |c'| label all grids in c' as c
                        else label all grids in c as c'
                    else if (h is translational) label h as in c
    end do
    */
    Ok(())
}

pub fn adjust_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update the density of all grids in grid_list
    /*
    foreach grid g whose attribute (dense/sparse/transitional) is changed since last call to adjust_clustering()
        if g is a sparse grid
            delete g from its cluster c, label g as NO_CLASS
            if (c becomes unconnected) split c into two clusters
        else if g is a dense grid
            among all neighboring grids of g, find out the grid h whoe cluster c_h has the largest size
            if h is a dense grid
                if (g is labelled as NO_CLASS) label g as in c_h
                else if (g is in cluster c and |c| > |c_h|)
                    label all grids in c_h as in c
                else if (g is in cluster c and |c| <= |c_h|)
                    label all grids in c as in c_h
            else if h is a transitional grid
                if ((g is NO_CLASS) and (h is an outside grid if g is added to c_h)) label g as in c_h
                else if (g is in cluster c and |c| >= |c_h|)
                    move h from cluster c_h to c
        else if (g is transitional grid)
            among neighboring clusters of g, find the largest one c'
            satisfying that g is an outside grid if added to it and
            label g as in c'
    */

    Ok(())
}<commit_msg>elaborate on some types<commit_after>use na::{Mat2, DMat};
//use na;
use std::num::*;
mod test;

#[test]
fn test_which_grid() {
    let loc = (-6., 6.);
    let i_rn = (-10.0, 10.0);
    let j_rn = (-10.0, 10.0);
    let i_bins = 10 as usize;
    let j_bins = 10 as usize;

    let result = grid_helpers::which_grid(loc, i_rn, j_rn, i_bins, j_bins).unwrap();
    assert_eq!((2, 8), result);

}

mod grid_helpers {
    pub fn which_grid(val: (f64, f64), i_range: (f64, f64), j_range: (f64, f64), i_bins: usize, j_bins: usize) -> Result<(usize, usize), String> {
        let i_size = (i_range.1 - i_range.0) / i_bins as f64;
        let i_number_of_sizes = ((val.0 - i_range.0) / i_size) as usize;
        let j_size = (j_range.1 - j_range.0) / j_bins as f64;
        let j_number_of_sizes = ((val.1 - j_range.0) / j_size) as usize;

        assert!(i_number_of_sizes < i_bins);
        assert!(j_number_of_sizes < j_bins);

        Ok((i_number_of_sizes, j_number_of_sizes))
    }
}

pub trait DensityGrid {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String>;

    fn compute_decay_coeff_at_time(&self, t_n: u32, t_l: u32, lambda: f64) -> f64 {
        lambda.powf((t_n - t_l) as f64)
    }
}

pub trait DensityGridSpace {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String>;

}

/*

input data: [(x, y, v)]

///some impl
for some time t in 0..:
  _.put t, [(x, y, v)]

//some other impl
fn put (t, [(x, y, v)]):
  [(i, j, sum_vals)] = [x,y,v].map(i,j,v).groupby(i,j).reduce(_+_)
  foreach:
    g = density_grids[i, j]
    g.update(t, sum_vals)
    print g.get()
*/
struct RawData {
    x: f64,
    y: f64,
    v: f64
}
struct TheWorld;
impl TheWorld {
    fn do_time_steps() {}
    fn do_one_time_step(t: u32, data: Vec<RawData>) {}
    fn put(t: u32, dat: Vec<RawData>) -> Result<(), String> {Ok(())}
    fn which_idxs(dat: RawData) -> Result<(usize, usize), String> {Ok((0,0))}
}
struct DG {
    i: usize,
    j: usize,
    density: f64,
    last_update: u32,
}
impl DG {
    fn update(t: u32, vals: Vec<f64>) {}
}

#[derive(Debug)]
#[derive(Copy)]
#[derive(Clone)]
pub struct GridPoint {
    i: usize, //location
    j: usize,
    density: f64,
    data_time: u32,
}

#[derive(Clone)]
pub struct Dat {
    x: f64,
    y: f64,
    val: f64,
    t: u32,
}

pub struct DStreamProps {
    c_m: f64,
    c_l: f64,
    lambda: f64,
    beta: f64,
    i_bins: usize,
    j_bins: usize,
    i_range: (f64, f64),
    j_range: (f64, f64),
}

pub struct TheGrid {
    mat: DMat<GridPoint>,
    props: DStreamProps,
    dats: Vec<((usize, usize), Dat)>,
}

impl Default for DStreamProps {
    fn default() -> DStreamProps {
        DStreamProps {
            c_m: 3.0,
            c_l: 0.8,
            lambda: 0.998,
            beta: 0.3,
            i_bins: 10 as usize,
            j_bins: 10 as usize,
            i_range: (-10.0, 10.0),
            j_range: (-10.0, 10.0),
        }
    }
}

impl DensityGrid for GridPoint {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String> {

        //TODO time - 1 is wrong, it should be biggest time smaller than current
        let coeff = self.compute_decay_coeff_at_time(time, time - 5, lambda);

        let sum_vals = dats.iter().fold(0.0, |sum, x| sum + x.1.val);
        println!("sum vals: {}", sum_vals);
        let new_d = coeff * sum_vals + 1.0;
        println!("new D: {}", new_d);

        Ok(new_d)
    }

//    fn update_with_values(&mut self, vals: &[f64], current_time: u32, lambda: f64) -> Result<(), String> {
//        //        D(g, t n ) = λ t n −t l D(g, t l ) + 1
//        let coeff = self.compute_decay_coeff_at_time(current_time, self.data_time, lambda);
//        println!("coeff: {}", coeff);
//        let sum_vals = vals.iter().fold(0.0, |sum, x| sum + x);
//        let new_d = coeff * coeff * sum_vals + 1.0;
//        println!("sum vals: {}", sum_vals);
//        println!("new D: {}", new_d);
//        self.density = new_d;
//        self.data_time = current_time;
//        Ok(())
//    }

}

impl DensityGridSpace for TheGrid {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String> {

//        println!("{}", self.density);
//        let props: DStreamProps = DStreamProps { ..Default::default() };
//        let idxs = grid_helpers::which_grid(val, props.i_range, props.j_range, props.i_bins, props.j_bins);
//        let res_update = self.update_grid_pt_density(val, 0, props.lambda);
//        println!("{}", self.density);

        if ((loc2d.0 <= self.props.i_range.1) &&
            (loc2d.0 >= self.props.i_range.0) &&
            (loc2d.1 <= self.props.j_range.1) &&
            (loc2d.1 >= self.props.j_range.0)) {

            println!("valid!");
            let idxs =
                grid_helpers::which_grid(loc2d, self.props.i_range, self.props.j_range,
                                         self.props.i_bins, self.props.j_bins).unwrap();
            let gp = &mut self.mat[idxs];

//            gp.update_with_values(&[100.0], t, self.props.lambda);

            self.dats.push((idxs, Dat {x: loc2d.0, y: loc2d.1, val: val, t:t}));

            println!("value at next time: {}",
                gp.get_value_at_time(
                    t+10,
                    self.props.lambda,
                        self.dats
                            .clone()
                            .into_iter()
                            .filter(|i| i.0 == idxs)
                            .collect::<Vec<((usize, usize), Dat)>>()).unwrap());

            println!("dats len: {}", self.dats.len());

            Ok(())
        } else {
            println!("invalid");
            Err(String::from("Invalid range"))
        }
    }
}
#[test]
fn test_put() {

    let mut grid_pt_1: GridPoint = GridPoint {i: 0, j: 0,
        density: 0.0, data_time: 0,};

    let mut grid_pt_2: GridPoint = GridPoint {i: 1, j: 0,
        density: 0.0, data_time: 0, };

    let mut grid_pt_3: GridPoint = GridPoint {i: 0, j: 1,
        density: 0.0, data_time: 0, };

    let mut grid_pt_4: GridPoint = GridPoint {i: 1, j: 1,
        density: 0.0, data_time: 0,};

    let mut v = Vec::new();
    for i in 0..100 {
        v.push(grid_pt_1)
    }

    let mut mat_thing = DMat::from_col_vec(
        10,
        10,
        &v
    );

    for i in 0..10 {

        for j in 0..10 {

            let mut g1= GridPoint
            {i: i, j: j,
                density: 0.0, data_time: 0, };

            mat_thing[(i,j)] = g1;
        }

    }

    let mut the_grid = TheGrid {mat: mat_thing, props: DStreamProps { ..Default::default() }, dats: Vec::new()};

    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 100).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1000).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10000).unwrap();

    for i in 0..10 {
        for j in 0..10 {
            let gp: &GridPoint = &the_grid.mat[(i as usize, j as usize)];
            assert_eq!(0.0, gp.density)
        }
    }
}

#[test]
fn test_initialize_clustering() {
    const loc_i: usize = 0 as usize;
    const loc_j: usize = 0 as usize;
    const G1: GridPoint = GridPoint
    {i: loc_i, j: loc_j,
        density: 0.0, data_time: 0, };
    let result = initialize_clustering(DMat::from_row_vec(2,2, &[G1, G1, G1, G1]));
    assert_eq!(Ok(()), result);
}

pub fn initialize_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update density of all grids in grid_list
    //assign each dense grid to a distinct cluster
    //label all other grids as NO_CLASS; bad grids!
    /*
    do until no change in cluster labels
          foreach cluster c
            foreach outside grid g of c
                foreach neighboring grid h of g
                    if h belongs to cluster c'
                        if |c| > |c'| label all grids in c' as c
                        else label all grids in c as c'
                    else if (h is translational) label h as in c
    end do
    */
    Ok(())
}

pub fn adjust_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update the density of all grids in grid_list
    /*
    foreach grid g whose attribute (dense/sparse/transitional) is changed since last call to adjust_clustering()
        if g is a sparse grid
            delete g from its cluster c, label g as NO_CLASS
            if (c becomes unconnected) split c into two clusters
        else if g is a dense grid
            among all neighboring grids of g, find out the grid h whoe cluster c_h has the largest size
            if h is a dense grid
                if (g is labelled as NO_CLASS) label g as in c_h
                else if (g is in cluster c and |c| > |c_h|)
                    label all grids in c_h as in c
                else if (g is in cluster c and |c| <= |c_h|)
                    label all grids in c as in c_h
            else if h is a transitional grid
                if ((g is NO_CLASS) and (h is an outside grid if g is added to c_h)) label g as in c_h
                else if (g is in cluster c and |c| >= |c_h|)
                    move h from cluster c_h to c
        else if (g is transitional grid)
            among neighboring clusters of g, find the largest one c'
            satisfying that g is an outside grid if added to it and
            label g as in c'
    */

    Ok(())
}<|endoftext|>


<commit_before>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// // example code where clippy issues a warning
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // example code which does not raise clippy warning
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if_chain! {
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
            let last = last_path_segment(qpath);
            if let Some(res) = last.res;
            if let Some(def_id) = res.opt_def_id();

            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::Unspecified,
                );
            }
        }
    }
}
<commit_msg>Update clippy_lints/src/ref_option_ref.rs doctest<commit_after>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if_chain! {
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
            let last = last_path_segment(qpath);
            if let Some(res) = last.res;
            if let Some(def_id) = res.opt_def_id();

            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::Unspecified,
                );
            }
        }
    }
}
<|endoftext|>