<commit_before>//! Endpoints for event filters.

pub mod create_filter;
pub mod get_filter;

mod lazy_load;
mod url;

pub use lazy_load::LazyLoadOptions;
pub use url::UrlFilter;

use js_int::UInt;
use ruma_identifiers::{RoomId, UserId};
use ruma_serde::{Outgoing, StringEnum};
use serde::Serialize;

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

    /// Raw events from federation.
    Federation,

    #[doc(hidden)]
    _Custom(String),
}

impl Default for EventFormat {
    fn default() -> Self {
        Self::Client
    }
}

/// Filters to be applied to room events.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(EventsWithUrl)`: Only events with a URL
    /// * `Some(EventsWithoutUrl)`: Only events without a URL
    #[serde(rename = "contains_url", skip_serializing_if = "Option::is_none")]
    pub url_filter: Option<UrlFilter>,

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

impl<'a> RoomEventFilter<'a> {
    /// Creates an empty `RoomEventFilter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.not_rooms.is_empty()
            && self.limit.is_none()
            && self.rooms.is_none()
            && self.not_senders.is_empty()
            && self.senders.is_none()
            && self.types.is_none()
            && self.url_filter.is_none()
            && self.lazy_load_options.is_disabled()
    }
}

impl IncomingRoomEventFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.not_rooms.is_empty()
            && self.limit.is_none()
            && self.rooms.is_none()
            && self.not_senders.is_empty()
            && self.senders.is_none()
            && self.types.is_none()
            && self.url_filter.is_none()
            && self.lazy_load_options.is_disabled()
    }
}

/// Filters to be applied to room data.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(default, skip_serializing_if = "ruma_serde::is_empty")]
    pub account_data: RoomEventFilter<'a>,

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

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

    /// The state events to include for rooms.
    #[serde(default, skip_serializing_if = "ruma_serde::is_empty")]
    pub state: 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> {
    /// Creates an empty `RoomFilter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        !self.include_leave
            && self.account_data.is_empty()
            && self.timeline.is_empty()
            && self.ephemeral.is_empty()
            && self.state.is_empty()
            && self.not_rooms.is_empty()
            && self.rooms.is_none()
    }
}

impl IncomingRoomFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        !self.include_leave
            && self.account_data.is_empty()
            && self.timeline.is_empty()
            && self.ephemeral.is_empty()
            && self.state.is_empty()
            && self.not_rooms.is_empty()
            && self.rooms.is_none()
    }
}

/// Filter for non-room data.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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> {
    /// Creates an empty `Filter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.limit.is_none()
            && self.senders.is_none()
            && self.types.is_none()
            && self.not_senders.is_empty()
    }
}

impl IncomingFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.limit.is_none()
            && self.senders.is_none()
            && self.types.is_none()
            && self.not_senders.is_empty()
    }
}

/// A filter definition
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(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(default, skip_serializing_if = "ruma_serde::is_default")]
    pub event_format: EventFormat,

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

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

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

impl<'a> FilterDefinition<'a> {
    /// Creates an empty `FilterDefinition`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

    /// Creates a `FilterDefinition` that can be used to ignore all events.
    pub fn ignore_all() -> Self {
        Self {
            account_data: Filter::ignore_all(),
            room: RoomFilter::ignore_all(),
            presence: Filter::ignore_all(),
            ..Default::default()
        }
    }

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.event_fields.is_none()
            && self.event_format == EventFormat::Client
            && self.presence.is_empty()
            && self.account_data.is_empty()
            && self.room.is_empty()
    }
}

impl IncomingFilterDefinition {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.event_fields.is_none()
            && self.event_format == EventFormat::Client
            && self.presence.is_empty()
            && self.account_data.is_empty()
            && self.room.is_empty()
    }
}

macro_rules! can_be_empty {
    ($ty:ident $(<$gen:tt>)?) => {
        impl $(<$gen>)? ruma_serde::CanBeEmpty for $ty $(<$gen>)? {
            fn is_empty(&self) -> bool {
                self.is_empty()
            }
        }
    };
}

can_be_empty!(Filter<'a>);
can_be_empty!(FilterDefinition<'a>);
can_be_empty!(RoomEventFilter<'a>);
can_be_empty!(RoomFilter<'a>);

can_be_empty!(IncomingFilter);
can_be_empty!(IncomingFilterDefinition);
can_be_empty!(IncomingRoomEventFilter);
can_be_empty!(IncomingRoomFilter);

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

    use super::{
        Filter, FilterDefinition, IncomingFilterDefinition, IncomingRoomFilter, RoomEventFilter,
        RoomFilter,
    };

    #[test]
    fn default_filters_are_empty() -> Result<(), serde_json::Error> {
        assert_eq!(to_json_value(Filter::default())?, json!({}));
        assert_eq!(to_json_value(FilterDefinition::default())?, json!({}));
        assert_eq!(to_json_value(RoomEventFilter::default())?, json!({}));
        assert_eq!(to_json_value(RoomFilter::default())?, json!({}));

        Ok(())
    }

    #[test]
    fn filter_definition_roundtrip() -> Result<(), serde_json::Error> {
        let filter = FilterDefinition::default();
        let filter_str = serde_json::to_value(&filter)?;

        let incoming_filter = serde_json::from_value::<IncomingFilterDefinition>(filter_str)?;
        assert!(incoming_filter.is_empty());

        Ok(())
    }

    #[test]
    fn room_filter_definition_roundtrip() -> Result<(), serde_json::Error> {
        let filter = RoomFilter::default();
        let room_filter = serde_json::to_value(&filter)?;

        let incoming_room_filter = serde_json::from_value::<IncomingRoomFilter>(room_filter)?;
        assert!(incoming_room_filter.is_empty());

        Ok(())
    }
}
<commit_msg>Remove needless uses of #[serde(default)]<commit_after>//! Endpoints for event filters.

pub mod create_filter;
pub mod get_filter;

mod lazy_load;
mod url;

pub use lazy_load::LazyLoadOptions;
pub use url::UrlFilter;

use js_int::UInt;
use ruma_identifiers::{RoomId, UserId};
use ruma_serde::{Outgoing, StringEnum};
use serde::Serialize;

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

    /// Raw events from federation.
    Federation,

    #[doc(hidden)]
    _Custom(String),
}

impl Default for EventFormat {
    fn default() -> Self {
        Self::Client
    }
}

/// Filters to be applied to room events.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(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(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(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(EventsWithUrl)`: Only events with a URL
    /// * `Some(EventsWithoutUrl)`: Only events without a URL
    #[serde(rename = "contains_url", skip_serializing_if = "Option::is_none")]
    pub url_filter: Option<UrlFilter>,

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

impl<'a> RoomEventFilter<'a> {
    /// Creates an empty `RoomEventFilter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.not_rooms.is_empty()
            && self.limit.is_none()
            && self.rooms.is_none()
            && self.not_senders.is_empty()
            && self.senders.is_none()
            && self.types.is_none()
            && self.url_filter.is_none()
            && self.lazy_load_options.is_disabled()
    }
}

impl IncomingRoomEventFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.not_rooms.is_empty()
            && self.limit.is_none()
            && self.rooms.is_none()
            && self.not_senders.is_empty()
            && self.senders.is_none()
            && self.types.is_none()
            && self.url_filter.is_none()
            && self.lazy_load_options.is_disabled()
    }
}

/// Filters to be applied to room data.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(default, skip_serializing_if = "ruma_serde::is_empty")]
    pub account_data: RoomEventFilter<'a>,

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

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

    /// The state events to include for rooms.
    #[serde(default, skip_serializing_if = "ruma_serde::is_empty")]
    pub state: 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(skip_serializing_if = "Option::is_none")]
    pub rooms: Option<&'a [RoomId]>,
}

impl<'a> RoomFilter<'a> {
    /// Creates an empty `RoomFilter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        !self.include_leave
            && self.account_data.is_empty()
            && self.timeline.is_empty()
            && self.ephemeral.is_empty()
            && self.state.is_empty()
            && self.not_rooms.is_empty()
            && self.rooms.is_none()
    }
}

impl IncomingRoomFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        !self.include_leave
            && self.account_data.is_empty()
            && self.timeline.is_empty()
            && self.ephemeral.is_empty()
            && self.state.is_empty()
            && self.not_rooms.is_empty()
            && self.rooms.is_none()
    }
}

/// Filter for non-room data.
#[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(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(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> {
    /// Creates an empty `Filter`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

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

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.limit.is_none()
            && self.senders.is_none()
            && self.types.is_none()
            && self.not_senders.is_empty()
    }
}

impl IncomingFilter {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.not_types.is_empty()
            && self.limit.is_none()
            && self.senders.is_none()
            && self.types.is_none()
            && self.not_senders.is_empty()
    }
}

/// A filter definition
#[derive(Clone, Debug, Default, Outgoing, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[incoming_derive(Clone, Default, 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(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(default, skip_serializing_if = "ruma_serde::is_default")]
    pub event_format: EventFormat,

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

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

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

impl<'a> FilterDefinition<'a> {
    /// Creates an empty `FilterDefinition`.
    ///
    /// You can also use the [`Default`] implementation.
    pub fn empty() -> Self {
        Self::default()
    }

    /// Creates a `FilterDefinition` that can be used to ignore all events.
    pub fn ignore_all() -> Self {
        Self {
            account_data: Filter::ignore_all(),
            room: RoomFilter::ignore_all(),
            presence: Filter::ignore_all(),
            ..Default::default()
        }
    }

    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.event_fields.is_none()
            && self.event_format == EventFormat::Client
            && self.presence.is_empty()
            && self.account_data.is_empty()
            && self.room.is_empty()
    }
}

impl IncomingFilterDefinition {
    /// Returns `true` if all fields are empty.
    pub fn is_empty(&self) -> bool {
        self.event_fields.is_none()
            && self.event_format == EventFormat::Client
            && self.presence.is_empty()
            && self.account_data.is_empty()
            && self.room.is_empty()
    }
}

macro_rules! can_be_empty {
    ($ty:ident $(<$gen:tt>)?) => {
        impl $(<$gen>)? ruma_serde::CanBeEmpty for $ty $(<$gen>)? {
            fn is_empty(&self) -> bool {
                self.is_empty()
            }
        }
    };
}

can_be_empty!(Filter<'a>);
can_be_empty!(FilterDefinition<'a>);
can_be_empty!(RoomEventFilter<'a>);
can_be_empty!(RoomFilter<'a>);

can_be_empty!(IncomingFilter);
can_be_empty!(IncomingFilterDefinition);
can_be_empty!(IncomingRoomEventFilter);
can_be_empty!(IncomingRoomFilter);

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

    use super::{
        Filter, FilterDefinition, IncomingFilterDefinition, IncomingRoomFilter, RoomEventFilter,
        RoomFilter,
    };

    #[test]
    fn default_filters_are_empty() -> Result<(), serde_json::Error> {
        assert_eq!(to_json_value(Filter::default())?, json!({}));
        assert_eq!(to_json_value(FilterDefinition::default())?, json!({}));
        assert_eq!(to_json_value(RoomEventFilter::default())?, json!({}));
        assert_eq!(to_json_value(RoomFilter::default())?, json!({}));

        Ok(())
    }

    #[test]
    fn filter_definition_roundtrip() -> Result<(), serde_json::Error> {
        let filter = FilterDefinition::default();
        let filter_str = serde_json::to_value(&filter)?;

        let incoming_filter = serde_json::from_value::<IncomingFilterDefinition>(filter_str)?;
        assert!(incoming_filter.is_empty());

        Ok(())
    }

    #[test]
    fn room_filter_definition_roundtrip() -> Result<(), serde_json::Error> {
        let filter = RoomFilter::default();
        let room_filter = serde_json::to_value(&filter)?;

        let incoming_room_filter = serde_json::from_value::<IncomingRoomFilter>(room_filter)?;
        assert!(incoming_room_filter.is_empty());

        Ok(())
    }
}
<|endoftext|>


<commit_before><commit_msg>virtio: video: Decouple EventQueue tests from the video-decoder feature.<commit_after><|endoftext|>


<commit_before>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Management of annotations.

use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, SerializeSeq, Serializer};
use serde_json::{self, Value};

use std::collections::HashMap;

use crate::plugins::PluginId;
use crate::view::View;
use crate::xi_rope::spans::{Spans, SpansBuilder};
use crate::xi_rope::{Interval, Rope};

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum AnnotationType {
    Selection,
    Find,
    Other(String),
}

impl AnnotationType {
    fn as_str(&self) -> &str {
        match self {
            AnnotationType::Find => "find",
            AnnotationType::Selection => "selection",
            AnnotationType::Other(ref s) => s,
        }
    }
}

/// Location and range of an annotation ([start_line, start_col, end_line, end_col]).
/// Location and range of an annotation
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct AnnotationRange {
    pub start_line: usize,
    pub start_col: usize,
    pub end_line: usize,
    pub end_col: usize,
}

impl Serialize for AnnotationRange {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut seq = serializer.serialize_seq(Some(4))?;
        seq.serialize_element(&self.start_line)?;
        seq.serialize_element(&self.start_col)?;
        seq.serialize_element(&self.end_line)?;
        seq.serialize_element(&self.end_col)?;
        seq.end()
    }
}

impl<'de> Deserialize<'de> for AnnotationRange {
    fn deserialize<D>(deserializer: D) -> Result<AnnotationRange, D::Error>
    where
        D: Deserializer<'de>,
    {
        let mut range = AnnotationRange { ..Default::default() };
        let seq = <[usize; 4]>::deserialize(deserializer)?;

        range.start_line = seq[0];
        range.start_col = seq[1];
        range.end_line = seq[2];
        range.end_col = seq[3];

        Ok(range)
    }
}

/// A set of annotations of a given type.
#[derive(Debug, Clone)]
pub struct Annotations {
    pub items: Spans<Value>,
    pub annotation_type: AnnotationType,
}

impl Annotations {
    /// Update the annotations in `interval` with the provided `items`.
    pub fn update(&mut self, interval: Interval, items: Spans<Value>) {
        self.items.edit(interval, items);
    }

    /// Remove annotations intersecting `interval`.
    pub fn invalidate(&mut self, interval: Interval) {
        self.items.delete_intersecting(interval);
    }
}

/// A region of an `Annotation`.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AnnotationSlice {
    annotation_type: AnnotationType,
    /// Annotation occurrences, guaranteed non-descending start order.
    ranges: Vec<AnnotationRange>,
    /// If present, one payload per range.
    payloads: Option<Vec<Value>>,
}

impl AnnotationSlice {
    pub fn new(
        annotation_type: AnnotationType,
        ranges: Vec<AnnotationRange>,
        payloads: Option<Vec<Value>>,
    ) -> Self {
        AnnotationSlice { annotation_type, ranges, payloads }
    }

    /// Returns json representation.
    pub fn to_json(&self) -> Value {
        json!({
            "type": self.annotation_type.as_str(),
            "ranges": self.ranges,
            "payloads": self.payloads,
            "n": self.ranges.len()
        })
    }

    fn to_annotations(&self, text: &Rope) -> Annotations {
        if self.ranges.is_empty() {
            // there are no annotations
            return Annotations {
                items: SpansBuilder::new(0).build(),
                annotation_type: self.annotation_type.clone(),
            };
        }

        let span_len = self
            .ranges
            .last()
            .map(|anno_range| text.offset_of_line(anno_range.end_line) + anno_range.end_col)
            .unwrap();

        let mut sb = SpansBuilder::new(span_len);

        for (i, &range) in self.ranges.iter().enumerate() {
            let payload = match &self.payloads {
                Some(p) => p[i].clone(),
                None => json!(null),
            };

            let start = text.offset_of_line(range.start_line) + range.start_col;
            let end = text.offset_of_line(range.end_line) + range.end_col;

            sb.add_span(Interval::new(start, end), payload);
        }

        Annotations { items: sb.build(), annotation_type: self.annotation_type.clone() }
    }
}

/// A trait for types (like `Selection`) that have a distinct representation
/// in core but are presented to the frontend as annotations.
pub trait ToAnnotation {
    /// Returns annotations that overlap the provided interval.
    fn get_annotations(&self, interval: Interval, view: &View, text: &Rope) -> AnnotationSlice;
}

/// All the annotations for a given view
pub struct AnnotationStore {
    store: HashMap<PluginId, Vec<Annotations>>,
}

impl AnnotationStore {
    pub fn new() -> Self {
        AnnotationStore { store: HashMap::new() }
    }

    /// Invalidates and removes all annotations in the range of the interval.
    pub fn invalidate(&mut self, interval: Interval) {
        self.store.values_mut().map(|v| v.iter_mut()).flatten().for_each(|a| a.invalidate(interval))
    }

    /// Applies an update from a plugin to a set of annotations
    pub fn update(&mut self, source: PluginId, interval: Interval, item: Annotations) {
        if !self.store.contains_key(&source) {
            self.store.insert(source, vec![item]);
            return;
        }

        let entry = self.store.get_mut(&source).unwrap();
        if let Some(annotation) =
            entry.iter_mut().find(|a| a.annotation_type == item.annotation_type)
        {
            annotation.update(interval, item.items);
        } else {
            entry.push(item);
        }
    }

    /// Returns an iterator which produces, for each type of annotation,
    /// those annotations which intersect the given interval.
    pub fn iter_range<'c>(
        &'c self,
        view: &'c View,
        text: &'c Rope,
        interval: Interval,
    ) -> impl Iterator<Item = AnnotationSlice> + 'c {
        self.store.iter().flat_map(move |(_plugin, value)| {
            value.iter().map(move |annotation| {
                let payloads = annotation
                    .items
                    .subseq(interval)
                    .iter()
                    .map(|(_i, p)| p.clone())
                    .collect::<Vec<Value>>();

                let ranges = annotation
                    .items
                    .subseq(interval)
                    .iter()
                    .map(|(i, _p)| {
                        let (start_line, start_col) = view.offset_to_line_col(text, i.start());
                        let (end_line, end_col) = view.offset_to_line_col(text, i.end());

                        AnnotationRange { start_line, start_col, end_line, end_col }
                    })
                    .collect::<Vec<AnnotationRange>>();

                AnnotationSlice {
                    annotation_type: annotation.annotation_type.clone(),
                    ranges,
                    payloads: Some(payloads),
                }
            })
        })
    }

    /// Removes any annotations provided by this plugin
    pub fn clear(&mut self, plugin: PluginId) {
        self.store.remove(&plugin);
    }
}

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

    #[test]
    fn test_annotation_range_serialization() {
        let range = AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 };

        assert_eq!(json!(range).to_string(), "[1,3,4,1]")
    }

    #[test]
    fn test_annotation_range_deserialization() {
        let range: AnnotationRange = serde_json::from_str("[1,3,4,1]").unwrap();
        assert_eq!(range, AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 })
    }

    #[test]
    fn test_annotation_slice_json() {
        let range = AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 };

        let slice = AnnotationSlice {
            annotation_type: AnnotationType::Find,
            ranges: vec![range],
            payloads: None,
        };

        assert_eq!(
            slice.to_json().to_string(),
            "{\"n\":1,\"payloads\":null,\"ranges\":[[1,3,4,1]],\"type\":\"find\"}"
        )
    }

    #[test]
    fn test_annotation_store_update() {
        let mut store = AnnotationStore::new();

        let mut sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(1, 5), json!(null));

        assert_eq!(store.store.len(), 0);

        store.update(
            PluginPid(1),
            Interval::new(1, 5),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 1);

        sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(6, 8), json!(null));

        store.update(
            PluginPid(2),
            Interval::new(6, 8),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 2);
    }

    #[test]
    fn test_annotation_store_clear() {
        let mut store = AnnotationStore::new();

        let mut sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(1, 5), json!(null));

        assert_eq!(store.store.len(), 0);

        store.update(
            PluginPid(1),
            Interval::new(1, 5),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 1);

        sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(6, 8), json!(null));

        store.update(
            PluginPid(2),
            Interval::new(6, 8),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 2);

        store.clear(PluginPid(1));

        assert_eq!(store.store.len(), 1);

        store.clear(PluginPid(1));

        assert_eq!(store.store.len(), 1);
    }
}
<commit_msg>Remove unused to_annotations method<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Management of annotations.

use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, SerializeSeq, Serializer};
use serde_json::{self, Value};

use std::collections::HashMap;

use crate::plugins::PluginId;
use crate::view::View;
use crate::xi_rope::spans::Spans;
use crate::xi_rope::{Interval, Rope};

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum AnnotationType {
    Selection,
    Find,
    Other(String),
}

impl AnnotationType {
    fn as_str(&self) -> &str {
        match self {
            AnnotationType::Find => "find",
            AnnotationType::Selection => "selection",
            AnnotationType::Other(ref s) => s,
        }
    }
}

/// Location and range of an annotation ([start_line, start_col, end_line, end_col]).
/// Location and range of an annotation
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct AnnotationRange {
    pub start_line: usize,
    pub start_col: usize,
    pub end_line: usize,
    pub end_col: usize,
}

impl Serialize for AnnotationRange {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut seq = serializer.serialize_seq(Some(4))?;
        seq.serialize_element(&self.start_line)?;
        seq.serialize_element(&self.start_col)?;
        seq.serialize_element(&self.end_line)?;
        seq.serialize_element(&self.end_col)?;
        seq.end()
    }
}

impl<'de> Deserialize<'de> for AnnotationRange {
    fn deserialize<D>(deserializer: D) -> Result<AnnotationRange, D::Error>
    where
        D: Deserializer<'de>,
    {
        let mut range = AnnotationRange { ..Default::default() };
        let seq = <[usize; 4]>::deserialize(deserializer)?;

        range.start_line = seq[0];
        range.start_col = seq[1];
        range.end_line = seq[2];
        range.end_col = seq[3];

        Ok(range)
    }
}

/// A set of annotations of a given type.
#[derive(Debug, Clone)]
pub struct Annotations {
    pub items: Spans<Value>,
    pub annotation_type: AnnotationType,
}

impl Annotations {
    /// Update the annotations in `interval` with the provided `items`.
    pub fn update(&mut self, interval: Interval, items: Spans<Value>) {
        self.items.edit(interval, items);
    }

    /// Remove annotations intersecting `interval`.
    pub fn invalidate(&mut self, interval: Interval) {
        self.items.delete_intersecting(interval);
    }
}

/// A region of an `Annotation`.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AnnotationSlice {
    annotation_type: AnnotationType,
    /// Annotation occurrences, guaranteed non-descending start order.
    ranges: Vec<AnnotationRange>,
    /// If present, one payload per range.
    payloads: Option<Vec<Value>>,
}

impl AnnotationSlice {
    pub fn new(
        annotation_type: AnnotationType,
        ranges: Vec<AnnotationRange>,
        payloads: Option<Vec<Value>>,
    ) -> Self {
        AnnotationSlice { annotation_type, ranges, payloads }
    }

    /// Returns json representation.
    pub fn to_json(&self) -> Value {
        json!({
            "type": self.annotation_type.as_str(),
            "ranges": self.ranges,
            "payloads": self.payloads,
            "n": self.ranges.len()
        })
    }
}

/// A trait for types (like `Selection`) that have a distinct representation
/// in core but are presented to the frontend as annotations.
pub trait ToAnnotation {
    /// Returns annotations that overlap the provided interval.
    fn get_annotations(&self, interval: Interval, view: &View, text: &Rope) -> AnnotationSlice;
}

/// All the annotations for a given view
pub struct AnnotationStore {
    store: HashMap<PluginId, Vec<Annotations>>,
}

impl AnnotationStore {
    pub fn new() -> Self {
        AnnotationStore { store: HashMap::new() }
    }

    /// Invalidates and removes all annotations in the range of the interval.
    pub fn invalidate(&mut self, interval: Interval) {
        self.store.values_mut().map(|v| v.iter_mut()).flatten().for_each(|a| a.invalidate(interval))
    }

    /// Applies an update from a plugin to a set of annotations
    pub fn update(&mut self, source: PluginId, interval: Interval, item: Annotations) {
        if !self.store.contains_key(&source) {
            self.store.insert(source, vec![item]);
            return;
        }

        let entry = self.store.get_mut(&source).unwrap();
        if let Some(annotation) =
            entry.iter_mut().find(|a| a.annotation_type == item.annotation_type)
        {
            annotation.update(interval, item.items);
        } else {
            entry.push(item);
        }
    }

    /// Returns an iterator which produces, for each type of annotation,
    /// those annotations which intersect the given interval.
    pub fn iter_range<'c>(
        &'c self,
        view: &'c View,
        text: &'c Rope,
        interval: Interval,
    ) -> impl Iterator<Item = AnnotationSlice> + 'c {
        self.store.iter().flat_map(move |(_plugin, value)| {
            value.iter().map(move |annotation| {
                let payloads = annotation
                    .items
                    .subseq(interval)
                    .iter()
                    .map(|(_i, p)| p.clone())
                    .collect::<Vec<Value>>();

                let ranges = annotation
                    .items
                    .subseq(interval)
                    .iter()
                    .map(|(i, _p)| {
                        let (start_line, start_col) = view.offset_to_line_col(text, i.start());
                        let (end_line, end_col) = view.offset_to_line_col(text, i.end());

                        AnnotationRange { start_line, start_col, end_line, end_col }
                    })
                    .collect::<Vec<AnnotationRange>>();

                AnnotationSlice {
                    annotation_type: annotation.annotation_type.clone(),
                    ranges,
                    payloads: Some(payloads),
                }
            })
        })
    }

    /// Removes any annotations provided by this plugin
    pub fn clear(&mut self, plugin: PluginId) {
        self.store.remove(&plugin);
    }
}

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

    #[test]
    fn test_annotation_range_serialization() {
        let range = AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 };

        assert_eq!(json!(range).to_string(), "[1,3,4,1]")
    }

    #[test]
    fn test_annotation_range_deserialization() {
        let range: AnnotationRange = serde_json::from_str("[1,3,4,1]").unwrap();
        assert_eq!(range, AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 })
    }

    #[test]
    fn test_annotation_slice_json() {
        let range = AnnotationRange { start_line: 1, start_col: 3, end_line: 4, end_col: 1 };

        let slice = AnnotationSlice {
            annotation_type: AnnotationType::Find,
            ranges: vec![range],
            payloads: None,
        };

        assert_eq!(
            slice.to_json().to_string(),
            "{\"n\":1,\"payloads\":null,\"ranges\":[[1,3,4,1]],\"type\":\"find\"}"
        )
    }

    #[test]
    fn test_annotation_store_update() {
        let mut store = AnnotationStore::new();

        let mut sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(1, 5), json!(null));

        assert_eq!(store.store.len(), 0);

        store.update(
            PluginPid(1),
            Interval::new(1, 5),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 1);

        sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(6, 8), json!(null));

        store.update(
            PluginPid(2),
            Interval::new(6, 8),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 2);
    }

    #[test]
    fn test_annotation_store_clear() {
        let mut store = AnnotationStore::new();

        let mut sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(1, 5), json!(null));

        assert_eq!(store.store.len(), 0);

        store.update(
            PluginPid(1),
            Interval::new(1, 5),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 1);

        sb = SpansBuilder::new(10);
        sb.add_span(Interval::new(6, 8), json!(null));

        store.update(
            PluginPid(2),
            Interval::new(6, 8),
            Annotations { annotation_type: AnnotationType::Find, items: sb.build() },
        );

        assert_eq!(store.store.len(), 2);

        store.clear(PluginPid(1));

        assert_eq!(store.store.len(), 1);

        store.clear(PluginPid(1));

        assert_eq!(store.store.len(), 1);
    }
}
<|endoftext|>


<commit_before>use Urgency;
use ffi;
use gdk_pixbuf;
use glib::translate::*;
use glib;
use glib_ffi;
use std;

glib_wrapper! {
    /// `Notification` represents a passive pop-up notification. It can contain
    /// summary text, body text, and an icon, as well as hints specifying how
    /// the notification should be presented. The notification is rendered by
    /// a notification daemon, and may present the notification in any number
    /// of ways. As such, there is a clear separation of content and
    /// presentation, and this API enforces that.
    pub struct Notification(Object<ffi::NotifyNotification>);

    match fn {
        get_type => || ffi::notify_notification_get_type(),
    }
}

impl Notification {
    /// Creates a new `Notification`. The summary text is required, but
    /// all other parameters are optional.
    /// ## `summary`
    /// The required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// The new `Notification`.
    pub fn new<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(summary: &str, body: P, icon: Q) -> Notification {
        assert_initialized_libnotify!();
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            from_glib_full(ffi::notify_notification_new(summary.to_glib_none().0, body.0, icon.0))
        }
    }

    /// Synchronously tells the notification server to hide the notification on the screen.
    ///
    /// # Returns
    ///
    /// `true` on success, or `false` on error with `error` filled in
    pub fn close(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_close(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Tells the notification server to display the notification on the screen.
    ///
    /// # Returns
    ///
    /// `true` if successful. On error, this will return `false` and set
    ///  `error`.
    // TODO: test if Error leaks memory
    pub fn show(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_show(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Sets a hint for `key` with value `value`. If `value` is `None`,
    /// a previously set hint for `key` is unset.
    ///
    /// If `value` is floating, it is consumed.
    /// ## `key`
    /// the hint key
    /// ## `value`
    pub fn set_hint(&self,
                key: &str,
                value: Option<glib::variant::Variant>) {
        assert_initialized_libnotify!();

        let gvalue: *mut glib_ffi::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            ffi::notify_notification_set_hint(self.to_glib_none().0,
                key.to_glib_none().0,
                gvalue)
        }
    }

    /// Sets the image in the notification from a `gdk_pixbuf::Pixbuf`.
    /// ## `pixbuf`
    /// The image.
    pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) {
        assert_initialized_libnotify!();

        unsafe {
            ffi::notify_notification_set_image_from_pixbuf(self.to_glib_none().0,
            pixbuf.to_glib_none().0);
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&self) {
        unsafe {
            ffi::notify_notification_clear_hints(self.to_glib_none().0);
        }
    }

    /// Sets the application name for the notification. If this function is
    /// not called or if `app_name` is `None`, the application name will be
    /// set from the value used in `notify_init` or overridden with
    /// `notify_set_app_name`.
    /// ## `app_name`
    /// the localised application name
    pub fn set_app_name<'a, P: Into<Option<&'a str>>>(&self, app_name: P) {
        let app_name = app_name.into();
        let app_name = app_name.to_glib_none();
        unsafe {
            ffi::notify_notification_set_app_name(self.to_glib_none().0, app_name.0);
        }
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    /// ## `category`
    /// The category.
    pub fn set_category(&self, category: &str) {
        unsafe {
            ffi::notify_notification_set_category(self.to_glib_none().0, category.to_glib_none().0);
        }
    }

    /// Sets the timeout of the notification. To set the default time, pass
    /// `NOTIFY_EXPIRES_DEFAULT` as `timeout`. To set the notification to never
    /// expire, pass `NOTIFY_EXPIRES_NEVER`.
    ///
    /// Note that the timeout may be ignored by the server.
    /// ## `timeout`
    /// The timeout in milliseconds.
    pub fn set_timeout(&self, timeout: i32) {
        unsafe {
            ffi::notify_notification_set_timeout(self.to_glib_none().0, timeout);
        }
    }

    /// Sets the urgency level of this notification.
    ///
    /// See: `Urgency`
    /// ## `urgency`
    /// The urgency level.
    pub fn set_urgency(&self, urgency: Urgency) {
        unsafe {
            ffi::notify_notification_set_urgency(self.to_glib_none().0, urgency.to_glib());
        }
    }

    /// Updates the notification text and icon. This won't send the update out
    /// and display it on the screen. For that, you will need to call
    /// `Notification::show`.
    /// ## `summary`
    /// The new required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// `true`, unless an invalid parameter was passed.
    pub fn update<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(&self, summary: &str, body: P, icon: Q) -> Result<(), glib::error::BoolError> {
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            glib::error::BoolError::from_glib(ffi::notify_notification_update(self.to_glib_none().0, summary.to_glib_none().0, body.0, icon.0), "Invalid parameter passed")
        }
    }
}
<commit_msg>Fix documentation<commit_after>use Urgency;
use ffi;
use gdk_pixbuf;
use glib::translate::*;
use glib;
use glib_ffi;
use std;

glib_wrapper! {
    /// `Notification` represents a passive pop-up notification. It can contain
    /// summary text, body text, and an icon, as well as hints specifying how
    /// the notification should be presented. The notification is rendered by
    /// a notification daemon, and may present the notification in any number
    /// of ways. As such, there is a clear separation of content and
    /// presentation, and this API enforces that.
    pub struct Notification(Object<ffi::NotifyNotification>);

    match fn {
        get_type => || ffi::notify_notification_get_type(),
    }
}

impl Notification {
    /// Creates a new `Notification`. The summary text is required, but
    /// all other parameters are optional.
    /// ## `summary`
    /// The required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// The new `Notification`.
    pub fn new<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(summary: &str, body: P, icon: Q) -> Notification {
        assert_initialized_libnotify!();
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            from_glib_full(ffi::notify_notification_new(summary.to_glib_none().0, body.0, icon.0))
        }
    }

    /// Synchronously tells the notification server to hide the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    pub fn close(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_close(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Tells the notification server to display the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    // TODO: test if Error leaks memory
    pub fn show(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_show(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Sets a hint for `key` with value `value`. If `value` is `None`,
    /// a previously set hint for `key` is unset.
    ///
    /// If `value` is floating, it is consumed.
    /// ## `key`
    /// the hint key
    /// ## `value`
    pub fn set_hint(&self,
                key: &str,
                value: Option<glib::variant::Variant>) {
        assert_initialized_libnotify!();

        let gvalue: *mut glib_ffi::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            ffi::notify_notification_set_hint(self.to_glib_none().0,
                key.to_glib_none().0,
                gvalue)
        }
    }

    /// Sets the image in the notification from a `gdk_pixbuf::Pixbuf`.
    /// ## `pixbuf`
    /// The image.
    pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) {
        assert_initialized_libnotify!();

        unsafe {
            ffi::notify_notification_set_image_from_pixbuf(self.to_glib_none().0,
            pixbuf.to_glib_none().0);
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&self) {
        unsafe {
            ffi::notify_notification_clear_hints(self.to_glib_none().0);
        }
    }

    /// Sets the application name for the notification. If this function is
    /// not called or if `app_name` is `None`, the application name will be
    /// set from the value used in `notify_init` or overridden with
    /// `notify_set_app_name`.
    /// ## `app_name`
    /// the localised application name
    pub fn set_app_name<'a, P: Into<Option<&'a str>>>(&self, app_name: P) {
        let app_name = app_name.into();
        let app_name = app_name.to_glib_none();
        unsafe {
            ffi::notify_notification_set_app_name(self.to_glib_none().0, app_name.0);
        }
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    /// ## `category`
    /// The category.
    pub fn set_category(&self, category: &str) {
        unsafe {
            ffi::notify_notification_set_category(self.to_glib_none().0, category.to_glib_none().0);
        }
    }

    /// Sets the timeout of the notification. To set the default time, pass
    /// `NOTIFY_EXPIRES_DEFAULT` as `timeout`. To set the notification to never
    /// expire, pass `NOTIFY_EXPIRES_NEVER`.
    ///
    /// Note that the timeout may be ignored by the server.
    /// ## `timeout`
    /// The timeout in milliseconds.
    pub fn set_timeout(&self, timeout: i32) {
        unsafe {
            ffi::notify_notification_set_timeout(self.to_glib_none().0, timeout);
        }
    }

    /// Sets the urgency level of this notification.
    ///
    /// See: `Urgency`
    /// ## `urgency`
    /// The urgency level.
    pub fn set_urgency(&self, urgency: Urgency) {
        unsafe {
            ffi::notify_notification_set_urgency(self.to_glib_none().0, urgency.to_glib());
        }
    }

    /// Updates the notification text and icon. This won't send the update out
    /// and display it on the screen. For that, you will need to call
    /// `Notification::show`.
    /// ## `summary`
    /// The new required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// `true`, unless an invalid parameter was passed.
    /// `Ok(())` on success, or `Err(err)` if an invalid parameter was passed
    pub fn update<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(&self, summary: &str, body: P, icon: Q) -> Result<(), glib::error::BoolError> {
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            glib::error::BoolError::from_glib(ffi::notify_notification_update(self.to_glib_none().0, summary.to_glib_none().0, body.0, icon.0), "Invalid parameter passed")
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, BufReader, BufRead};
use std::mem;
use compileerror::{Pos, Span, CompileResult, ErrorCode, err};
use parser::{TokenQueue, TokenKind, Operator, Token};

#[derive(Debug, Clone, Eq, PartialEq)]
enum LexState
{
    Idle,
    Comment,
    Identifier,
    Number,
    Operator,
    InString,
}

pub struct Lexer
{
    state: LexState,
    tokens: TokenQueue,
    pos: Pos,
    token_start_pos: Pos,
    data: String,
    escape_code: bool,
}

fn is_operator_start(c: char) -> bool
{
    for &op in ['+', '-', '*', '/', '%', '>', '<', '=', '!', '.', '|', '&', ':'].iter()
    {
        if op == c {return true;}
    }

    false
}

fn is_identifier_start(c: char) -> bool
{
    c.is_alphanumeric() || c == '_'
}

impl Lexer
{
    pub fn new() -> Lexer
    {
        Lexer {
            state: LexState::Idle,
            tokens: TokenQueue::new(),
            pos: Pos::new(1, 1),
            token_start_pos: Pos::new(1, 1),
            data: String::new(),
            escape_code: false,
        }
    }

    fn start(&mut self, c: char, ns: LexState)
    {
        self.token_start_pos = self.pos;
        self.state = ns;
        self.data.clear();
        if self.state != LexState::InString {self.data.push(c);}
    }

    fn idle(&mut self, c: char) -> CompileResult<()>
    {
        let pos = self.pos;
        match c
        {
            '\n' | ' ' | '\t' => Ok(()),
            '#' => {self.state = LexState::Comment; Ok(())},
            ',' => {self.add(TokenKind::Comma, Span::single(pos)); Ok(())},
            '(' => {self.add(TokenKind::OpenParen, Span::single(pos)); Ok(())},
            ')' => {self.add(TokenKind::CloseParen, Span::single(pos)); Ok(())},
            '{' => {self.add(TokenKind::OpenCurly, Span::single(pos)); Ok(())},
            '}' => {self.add(TokenKind::CloseCurly, Span::single(pos)); Ok(())},
            '[' => {self.add(TokenKind::OpenBracket, Span::single(pos)); Ok(())},
            ']' => {self.add(TokenKind::CloseBracket, Span::single(pos)); Ok(())},
            '@' => {self.add(TokenKind::Lambda, Span::single(pos)); Ok(())},
            '$' => {self.add(TokenKind::Dollar, Span::single(pos)); Ok(())},
            ';' => {self.add(TokenKind::SemiColon, Span::single(pos)); Ok(())},
            '0'...'9' => {self.start(c, LexState::Number); Ok(())},
            '\"' => {self.start(c, LexState::InString); Ok(())},
            ch if is_identifier_start(ch) => {self.start(c, LexState::Identifier); Ok(())},
            ch if is_operator_start(ch) => {self.start(c, LexState::Operator); Ok(())}
            _ => {
                err(self.pos, ErrorCode::UnexpectedChar, format!("Unexpected char {}", c))
            }
        }
    }

    fn comment(&mut self, c: char) -> CompileResult<()>
    {
        if c == '\n' {self.state = LexState::Idle;}
        Ok(())
    }

    fn add_identifier(&mut self) -> TokenKind
    {
        let tok = match &self.data[..]
        {
            "import" => TokenKind::Import,
            "match" => TokenKind::Match,
            "true" => TokenKind::True,
            "false" => TokenKind::False,
            _ => TokenKind::Identifier(mem::replace(&mut self.data, String::new())),
        };

        self.data.clear();
        tok
    }

    fn current_span(&self) -> Span
    {
        Span::new(self.token_start_pos, Pos::new(self.pos.line, self.pos.offset - 1))
    }

    fn identifier(&mut self, c: char) -> CompileResult<()>
    {
        if is_identifier_start(c)
        {
            self.data.push(c);
            Ok(())
        }
        else
        {
            self.state = LexState::Idle;
            let span = self.current_span();
            let tok = self.add_identifier();
            self.add(tok, span);
            self.idle(c)
        }
    }

    fn number(&mut self, c: char) -> CompileResult<()>
    {
        if c.is_numeric()
        {
            self.data.push(c);
            Ok(())
        }
        else
        {
            self.state = LexState::Idle;
            let span = self.current_span();
            let num = mem::replace(&mut self.data, String::new());
            self.add(TokenKind::Number(num), span);
            self.idle(c)
        }
    }

    fn data_to_token_kind(&self) -> CompileResult<TokenKind>
    {
        match &self.data[..]
        {
            "+" => Ok(TokenKind::Operator(Operator::Add)),
            "-" => Ok(TokenKind::Operator(Operator::Sub)),
            "*" => Ok(TokenKind::Operator(Operator::Mul)),
            "/" => Ok(TokenKind::Operator(Operator::Div)),
            "%" => Ok(TokenKind::Operator(Operator::Mod)),
            ">" => Ok(TokenKind::Operator(Operator::GreaterThan)),
            ">=" => Ok(TokenKind::Operator(Operator::GreaterThanEquals)),
            "<" => Ok(TokenKind::Operator(Operator::LessThan)),
            "<=" => Ok(TokenKind::Operator(Operator::LessThanEquals)),
            "=" => Ok(TokenKind::Assign),
            "==" => Ok(TokenKind::Operator(Operator::Equals)),
            "!" => Ok(TokenKind::Operator(Operator::Not)),
            "!=" => Ok(TokenKind::Operator(Operator::NotEquals)),
            "&&" => Ok(TokenKind::Operator(Operator::And)),
            "||" => Ok(TokenKind::Operator(Operator::Or)),
            "->" => Ok(TokenKind::Arrow),
            "=>" => Ok(TokenKind::FatArrow),
            ":" => Ok(TokenKind::Colon),
            "::" => Ok(TokenKind::DoubleColon),
            "|" => Ok(TokenKind::Pipe),
            "++" => Ok(TokenKind::Operator(Operator::Concat)),
            _ => err(self.pos, ErrorCode::InvalidOperator, format!("Invalid operator {}", self.data)),
        }
    }

    fn operator(&mut self, c: char) -> CompileResult<()>
    {
        if c.is_whitespace() || c.is_alphanumeric() || c == '{' || c == '(' || c == '['
        {
            let kind = try!(self.data_to_token_kind());
            self.state = LexState::Idle;
            let span = self.current_span();
            self.add(kind, span);
            self.idle(c)
        }
        else
        {
            self.data.push(c);
            Ok(())
        }
    }

    fn in_string(&mut self, c: char) -> CompileResult<()>
    {
        if self.escape_code
        {
            self.escape_code = false;
            match c
            {
                'r' => self.data.push('\r'),
                'n' => self.data.push('\n'),
                't' => self.data.push('\n'),
                _   => self.data.push(c),
            }

            Ok(())
        }
        else if c == '\\'
        {
            self.escape_code = true;
            Ok(())
        }
        else if c == '"'
        {
            let s = mem::replace(&mut self.data, String::new());
            let mut span = self.current_span();
            span.end.offset += 1; // Need to include the quote
            self.add(TokenKind::StringLiteral(s), span);
            self.escape_code = false;
            self.state = LexState::Idle;
            Ok(())
        }
        else
        {
            self.data.push(c);
            Ok(())
        }
    }

    fn feed(&mut self, c: char) -> CompileResult<()>
    {
        match self.state
        {
            LexState::Idle => self.idle(c),
            LexState::Comment => self.comment(c),
            LexState::Identifier => self.identifier(c),
            LexState::Number =>  self.number(c),
            LexState::Operator => self.operator(c),
            LexState::InString => self.in_string(c),
        }
    }

    fn add(&mut self, tok: TokenKind, span: Span)
    {
        self.tokens.add(Token::new(tok, span));
    }

    pub fn read<Input: Read>(&mut self, input: &mut Input) -> CompileResult<TokenQueue>
    {
        for line in BufReader::new(input).lines()
        {
            for c in try!(line).chars()
            {
                try!(self.feed(c));
                self.pos.offset += 1;
            }

            try!(self.feed('\n'));
            self.pos.offset = 1;
            self.pos.line += 1;
        }

        let pos = self.pos;
        self.add(TokenKind::EOF, Span::single(pos));
        //self.tokens.dump();
        Ok(mem::replace(&mut self.tokens, TokenQueue::new()))
    }
}

#[cfg(test)]
mod tests
{
    use std::io::Cursor;
    use parser::*;
    use compileerror::*;

    fn tok(kind: TokenKind, sline: usize, soffset: usize, eline: usize, eoffset: usize) -> Token
    {
        Token::new(kind, span(sline, soffset, eline, eoffset))
    }

    #[test]
    fn test_keywords()
    {
        let mut cursor = Cursor::new("import match");
        let mut lexer = Lexer::new();
        let tokens: Vec<Token> = lexer
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Import, 1, 1, 1, 6),
            tok(TokenKind::Match, 1, 8, 1, 12),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_identifiers_and_numbers()
    {
        let mut cursor = Cursor::new("blaat 8888 _foo_16");
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Identifier("blaat".into()), 1, 1, 1, 5),
            tok(TokenKind::Number("8888".into()), 1, 7, 1, 10),
            tok(TokenKind::Identifier("_foo_16".into()), 1, 12, 1, 18),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_specials()
    {
        let mut cursor = Cursor::new("+ - * / % < <= > >= == = != ! || && => -> : , :: $ @");
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Operator(Operator::Add), 1, 1, 1, 1),
            tok(TokenKind::Operator(Operator::Sub), 1, 3, 1, 3),
            tok(TokenKind::Operator(Operator::Mul), 1, 5, 1, 5),
            tok(TokenKind::Operator(Operator::Div), 1, 7, 1, 7),
            tok(TokenKind::Operator(Operator::Mod), 1, 9, 1, 9),
            tok(TokenKind::Operator(Operator::LessThan), 1, 11, 1, 11),
            tok(TokenKind::Operator(Operator::LessThanEquals), 1, 13, 1, 14),
            tok(TokenKind::Operator(Operator::GreaterThan), 1, 16, 1, 16),
            tok(TokenKind::Operator(Operator::GreaterThanEquals), 1, 18, 1, 19),
            tok(TokenKind::Operator(Operator::Equals), 1, 21, 1, 22),
            tok(TokenKind::Assign, 1, 24, 1, 24),
            tok(TokenKind::Operator(Operator::NotEquals), 1, 26, 1, 27),
            tok(TokenKind::Operator(Operator::Not), 1, 29, 1, 29),
            tok(TokenKind::Operator(Operator::Or), 1, 31, 1, 32),
            tok(TokenKind::Operator(Operator::And), 1, 34, 1, 35),
            tok(TokenKind::FatArrow, 1, 37, 1, 38),
            tok(TokenKind::Arrow, 1, 40, 1, 41),
            tok(TokenKind::Colon, 1, 43, 1, 43),
            tok(TokenKind::Comma, 1, 45, 1, 45),
            tok(TokenKind::DoubleColon, 1, 47, 1, 48),
            tok(TokenKind::Dollar, 1, 50, 1, 50),
            tok(TokenKind::Lambda, 1, 52, 1, 52),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_string()
    {
        let mut cursor = Cursor::new(r#""This is a string" "Blaat\n" "$a""#);
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::StringLiteral("This is a string".into()), 1, 1, 1, 18),
            tok(TokenKind::StringLiteral("Blaat\n".into()), 1, 20, 1, 28),
            tok(TokenKind::StringLiteral("$a".into()), 1, 30, 1, 33),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }
}
<commit_msg>Fix floating point parsing<commit_after>use std::io::{Read, BufReader, BufRead};
use std::mem;
use compileerror::{Pos, Span, CompileResult, ErrorCode, err};
use parser::{TokenQueue, TokenKind, Operator, Token};

#[derive(Debug, Clone, Eq, PartialEq)]
enum LexState
{
    Idle,
    Comment,
    Identifier,
    Number,
    Operator,
    InString,
}

pub struct Lexer
{
    state: LexState,
    tokens: TokenQueue,
    pos: Pos,
    token_start_pos: Pos,
    data: String,
    escape_code: bool,
}

fn is_operator_start(c: char) -> bool
{
    for &op in ['+', '-', '*', '/', '%', '>', '<', '=', '!', '.', '|', '&', ':'].iter()
    {
        if op == c {return true;}
    }

    false
}

fn is_identifier_start(c: char) -> bool
{
    c.is_alphanumeric() || c == '_'
}

impl Lexer
{
    pub fn new() -> Lexer
    {
        Lexer {
            state: LexState::Idle,
            tokens: TokenQueue::new(),
            pos: Pos::new(1, 1),
            token_start_pos: Pos::new(1, 1),
            data: String::new(),
            escape_code: false,
        }
    }

    fn start(&mut self, c: char, ns: LexState)
    {
        self.token_start_pos = self.pos;
        self.state = ns;
        self.data.clear();
        if self.state != LexState::InString {self.data.push(c);}
    }

    fn idle(&mut self, c: char) -> CompileResult<()>
    {
        let pos = self.pos;
        match c
        {
            '\n' | ' ' | '\t' => Ok(()),
            '#' => {self.state = LexState::Comment; Ok(())},
            ',' => {self.add(TokenKind::Comma, Span::single(pos)); Ok(())},
            '(' => {self.add(TokenKind::OpenParen, Span::single(pos)); Ok(())},
            ')' => {self.add(TokenKind::CloseParen, Span::single(pos)); Ok(())},
            '{' => {self.add(TokenKind::OpenCurly, Span::single(pos)); Ok(())},
            '}' => {self.add(TokenKind::CloseCurly, Span::single(pos)); Ok(())},
            '[' => {self.add(TokenKind::OpenBracket, Span::single(pos)); Ok(())},
            ']' => {self.add(TokenKind::CloseBracket, Span::single(pos)); Ok(())},
            '@' => {self.add(TokenKind::Lambda, Span::single(pos)); Ok(())},
            '$' => {self.add(TokenKind::Dollar, Span::single(pos)); Ok(())},
            ';' => {self.add(TokenKind::SemiColon, Span::single(pos)); Ok(())},
            '0'...'9' => {self.start(c, LexState::Number); Ok(())},
            '\"' => {self.start(c, LexState::InString); Ok(())},
            ch if is_identifier_start(ch) => {self.start(c, LexState::Identifier); Ok(())},
            ch if is_operator_start(ch) => {self.start(c, LexState::Operator); Ok(())}
            _ => {
                err(self.pos, ErrorCode::UnexpectedChar, format!("Unexpected char {}", c))
            }
        }
    }

    fn comment(&mut self, c: char) -> CompileResult<()>
    {
        if c == '\n' {self.state = LexState::Idle;}
        Ok(())
    }

    fn add_identifier(&mut self) -> TokenKind
    {
        let tok = match &self.data[..]
        {
            "import" => TokenKind::Import,
            "match" => TokenKind::Match,
            "true" => TokenKind::True,
            "false" => TokenKind::False,
            _ => TokenKind::Identifier(mem::replace(&mut self.data, String::new())),
        };

        self.data.clear();
        tok
    }

    fn current_span(&self) -> Span
    {
        Span::new(self.token_start_pos, Pos::new(self.pos.line, self.pos.offset - 1))
    }

    fn identifier(&mut self, c: char) -> CompileResult<()>
    {
        if is_identifier_start(c)
        {
            self.data.push(c);
            Ok(())
        }
        else
        {
            self.state = LexState::Idle;
            let span = self.current_span();
            let tok = self.add_identifier();
            self.add(tok, span);
            self.idle(c)
        }
    }

    fn number(&mut self, c: char) -> CompileResult<()>
    {
        if c.is_numeric() || c == '.' || c == 'e'
        {
            self.data.push(c);
            Ok(())
        }
        else
        {
            self.state = LexState::Idle;
            let span = self.current_span();
            let num = mem::replace(&mut self.data, String::new());
            self.add(TokenKind::Number(num), span);
            self.idle(c)
        }
    }

    fn data_to_token_kind(&self) -> CompileResult<TokenKind>
    {
        match &self.data[..]
        {
            "+" => Ok(TokenKind::Operator(Operator::Add)),
            "-" => Ok(TokenKind::Operator(Operator::Sub)),
            "*" => Ok(TokenKind::Operator(Operator::Mul)),
            "/" => Ok(TokenKind::Operator(Operator::Div)),
            "%" => Ok(TokenKind::Operator(Operator::Mod)),
            ">" => Ok(TokenKind::Operator(Operator::GreaterThan)),
            ">=" => Ok(TokenKind::Operator(Operator::GreaterThanEquals)),
            "<" => Ok(TokenKind::Operator(Operator::LessThan)),
            "<=" => Ok(TokenKind::Operator(Operator::LessThanEquals)),
            "=" => Ok(TokenKind::Assign),
            "==" => Ok(TokenKind::Operator(Operator::Equals)),
            "!" => Ok(TokenKind::Operator(Operator::Not)),
            "!=" => Ok(TokenKind::Operator(Operator::NotEquals)),
            "&&" => Ok(TokenKind::Operator(Operator::And)),
            "||" => Ok(TokenKind::Operator(Operator::Or)),
            "->" => Ok(TokenKind::Arrow),
            "=>" => Ok(TokenKind::FatArrow),
            ":" => Ok(TokenKind::Colon),
            "::" => Ok(TokenKind::DoubleColon),
            "|" => Ok(TokenKind::Pipe),
            "++" => Ok(TokenKind::Operator(Operator::Concat)),
            _ => err(self.pos, ErrorCode::InvalidOperator, format!("Invalid operator {}", self.data)),
        }
    }

    fn operator(&mut self, c: char) -> CompileResult<()>
    {
        if c.is_whitespace() || c.is_alphanumeric() || c == '{' || c == '(' || c == '['
        {
            let kind = try!(self.data_to_token_kind());
            self.state = LexState::Idle;
            let span = self.current_span();
            self.add(kind, span);
            self.idle(c)
        }
        else
        {
            self.data.push(c);
            Ok(())
        }
    }

    fn in_string(&mut self, c: char) -> CompileResult<()>
    {
        if self.escape_code
        {
            self.escape_code = false;
            match c
            {
                'r' => self.data.push('\r'),
                'n' => self.data.push('\n'),
                't' => self.data.push('\n'),
                _   => self.data.push(c),
            }

            Ok(())
        }
        else if c == '\\'
        {
            self.escape_code = true;
            Ok(())
        }
        else if c == '"'
        {
            let s = mem::replace(&mut self.data, String::new());
            let mut span = self.current_span();
            span.end.offset += 1; // Need to include the quote
            self.add(TokenKind::StringLiteral(s), span);
            self.escape_code = false;
            self.state = LexState::Idle;
            Ok(())
        }
        else
        {
            self.data.push(c);
            Ok(())
        }
    }

    fn feed(&mut self, c: char) -> CompileResult<()>
    {
        match self.state
        {
            LexState::Idle => self.idle(c),
            LexState::Comment => self.comment(c),
            LexState::Identifier => self.identifier(c),
            LexState::Number =>  self.number(c),
            LexState::Operator => self.operator(c),
            LexState::InString => self.in_string(c),
        }
    }

    fn add(&mut self, tok: TokenKind, span: Span)
    {
        self.tokens.add(Token::new(tok, span));
    }

    pub fn read<Input: Read>(&mut self, input: &mut Input) -> CompileResult<TokenQueue>
    {
        for line in BufReader::new(input).lines()
        {
            for c in try!(line).chars()
            {
                try!(self.feed(c));
                self.pos.offset += 1;
            }

            try!(self.feed('\n'));
            self.pos.offset = 1;
            self.pos.line += 1;
        }

        let pos = self.pos;
        self.add(TokenKind::EOF, Span::single(pos));
        //self.tokens.dump();
        Ok(mem::replace(&mut self.tokens, TokenQueue::new()))
    }
}

#[cfg(test)]
mod tests
{
    use std::io::Cursor;
    use parser::*;
    use compileerror::*;

    fn tok(kind: TokenKind, sline: usize, soffset: usize, eline: usize, eoffset: usize) -> Token
    {
        Token::new(kind, span(sline, soffset, eline, eoffset))
    }

    #[test]
    fn test_keywords()
    {
        let mut cursor = Cursor::new("import match");
        let mut lexer = Lexer::new();
        let tokens: Vec<Token> = lexer
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Import, 1, 1, 1, 6),
            tok(TokenKind::Match, 1, 8, 1, 12),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_identifiers_and_numbers()
    {
        let mut cursor = Cursor::new("blaat 8888 _foo_16");
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Identifier("blaat".into()), 1, 1, 1, 5),
            tok(TokenKind::Number("8888".into()), 1, 7, 1, 10),
            tok(TokenKind::Identifier("_foo_16".into()), 1, 12, 1, 18),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_specials()
    {
        let mut cursor = Cursor::new("+ - * / % < <= > >= == = != ! || && => -> : , :: $ @");
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::Operator(Operator::Add), 1, 1, 1, 1),
            tok(TokenKind::Operator(Operator::Sub), 1, 3, 1, 3),
            tok(TokenKind::Operator(Operator::Mul), 1, 5, 1, 5),
            tok(TokenKind::Operator(Operator::Div), 1, 7, 1, 7),
            tok(TokenKind::Operator(Operator::Mod), 1, 9, 1, 9),
            tok(TokenKind::Operator(Operator::LessThan), 1, 11, 1, 11),
            tok(TokenKind::Operator(Operator::LessThanEquals), 1, 13, 1, 14),
            tok(TokenKind::Operator(Operator::GreaterThan), 1, 16, 1, 16),
            tok(TokenKind::Operator(Operator::GreaterThanEquals), 1, 18, 1, 19),
            tok(TokenKind::Operator(Operator::Equals), 1, 21, 1, 22),
            tok(TokenKind::Assign, 1, 24, 1, 24),
            tok(TokenKind::Operator(Operator::NotEquals), 1, 26, 1, 27),
            tok(TokenKind::Operator(Operator::Not), 1, 29, 1, 29),
            tok(TokenKind::Operator(Operator::Or), 1, 31, 1, 32),
            tok(TokenKind::Operator(Operator::And), 1, 34, 1, 35),
            tok(TokenKind::FatArrow, 1, 37, 1, 38),
            tok(TokenKind::Arrow, 1, 40, 1, 41),
            tok(TokenKind::Colon, 1, 43, 1, 43),
            tok(TokenKind::Comma, 1, 45, 1, 45),
            tok(TokenKind::DoubleColon, 1, 47, 1, 48),
            tok(TokenKind::Dollar, 1, 50, 1, 50),
            tok(TokenKind::Lambda, 1, 52, 1, 52),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }

    #[test]
    fn test_string()
    {
        let mut cursor = Cursor::new(r#""This is a string" "Blaat\n" "$a""#);
        let tokens: Vec<Token> = Lexer::new()
            .read(&mut cursor)
            .expect("Lexing failed")
            .collect();

        assert_eq!(tokens, vec![
            tok(TokenKind::StringLiteral("This is a string".into()), 1, 1, 1, 18),
            tok(TokenKind::StringLiteral("Blaat\n".into()), 1, 20, 1, 28),
            tok(TokenKind::StringLiteral("$a".into()), 1, 30, 1, 33),
            tok(TokenKind::EOF, 2, 1, 2, 1),
        ]);
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    use model::{Model, TemplateParameter};

    use std::fs::File;
    use std::io::Write;
    use clang::{Clang, Index};
    use tempdir::TempDir;

    use serde_json;

    const INTERFACE: &'static str = r#"
namespace foo { namespace sample {

struct Interface {
    virtual ~Interface() = default;
    virtual void method(int foo) = 0;
    virtual int foo(double bar) = 0;
    virtual bool baz() {return true};
};

}}
"#;

    const TEMPLATE_INTERFACE: &'static str = r#"
namespace foo { namespace bar {

template <typename T, typename U, class V>
class Baz {
    virtual ~Baz() = default;
    Baz(const Baz&) = delete;
    virtual bool boo() = 0;
};

}}
"#;

    fn create_model(input: &str) -> Model {
        let tmp_dir = TempDir::new("cpp-codegen").expect("create temp dir");
        let file_path = tmp_dir.path().join("interface.h");
        let mut tmp_file = File::create(&file_path).expect("create temp file");
        tmp_file.write(input.as_bytes()).expect("write file");
        let clang = Clang::new().expect("instantiate clang");
        let index = Index::new(&clang, false, false);
        let tu = index.parser(&file_path)
            .arguments(&[&"-x", &"c++", &"-std=c++14"])
            .parse()
            .expect("parse interface");
        Model::new(&tu)
    }

    #[test]
    fn should_parse() {
        create_model(INTERFACE);
    }

    #[test]
    fn should_not_include_destructors() {
        let model = create_model(INTERFACE);
        assert!(!model.interfaces[0]
            .clone()
            .methods
            .into_iter()
            .any(|x| x.name == "~Interface"));
    }

    #[test]
    fn should_parse_template_class() {
        create_model(TEMPLATE_INTERFACE);
    }

    #[test]
    fn should_list_template_parameters() {
        let model = create_model(TEMPLATE_INTERFACE);
        assert!(model.interfaces.len() > 0);
        assert_eq!(model.interfaces[0]
                       .clone()
                       .template_parameters
                       .unwrap(),
                   vec![TemplateParameter { type_name: "T".to_string() },
                        TemplateParameter { type_name: "U".to_string() },
                        TemplateParameter { type_name: "V".to_string() }]);
    }

    #[test]
    fn should_generate_random_names_for_anonymous_method_arguments() {

        const INTERFACE_WITH_NAMELESS_ARGUMENT_METHODS: &'static str = r#"
            struct Foo {
                virtual void noArgumentName(double) = 0;
            };
            "#;

        let model = create_model(INTERFACE_WITH_NAMELESS_ARGUMENT_METHODS);
        assert!(serde_json::ser::to_string(&model.interfaces[0].methods[0].arguments[0].name)
            .unwrap()
            .len() > 0)
    }

    #[test]
    fn should_parse_template_methods() {

        const INTERFACE_WITH_TEMPLATE_METHODS: &'static str = r#"
            struct Foo {
                template <typename T> void foo(T bar);
            };
            "#;

        let model = create_model(INTERFACE_WITH_TEMPLATE_METHODS);
        assert_eq!(model.interfaces[0]
                           .clone()
                           .methods[0]
                       .clone()
                       .template_arguments
                       .unwrap(),
                   vec![TemplateParameter { type_name: "T".to_string() }])
    }
}
<commit_msg>Add tests for namespaced method parameters<commit_after>#[cfg(test)]
mod tests {
    use model::{Model, TemplateParameter};

    use std::fs::File;
    use std::io::Write;
    use clang::{Clang, Index};
    use tempdir::TempDir;

    use serde_json;

    const INTERFACE: &'static str = r#"
namespace foo { namespace sample {

struct Interface {
    virtual ~Interface() = default;
    virtual void method(int foo) = 0;
    virtual int foo(double bar) = 0;
    virtual bool baz() {return true};
};

}}
"#;

    const TEMPLATE_INTERFACE: &'static str = r#"
namespace foo { namespace bar {

template <typename T, typename U, class V>
class Baz {
    virtual ~Baz() = default;
    Baz(const Baz&) = delete;
    virtual bool boo() = 0;
};

}}
"#;

    fn create_model(input: &str) -> Model {
        let tmp_dir = TempDir::new("cpp-codegen").expect("create temp dir");
        let file_path = tmp_dir.path().join("interface.h");
        let mut tmp_file = File::create(&file_path).expect("create temp file");
        tmp_file.write(input.as_bytes()).expect("write file");
        let clang = Clang::new().expect("instantiate clang");
        let index = Index::new(&clang, false, false);
        let tu = index.parser(&file_path)
            .arguments(&[&"-x", &"c++", &"-std=c++14"])
            .parse()
            .expect("parse interface");
        Model::new(&tu)
    }

    #[test]
    fn should_parse() {
        create_model(INTERFACE);
    }

    #[test]
    fn should_not_include_destructors() {
        let model = create_model(INTERFACE);
        assert!(!model.interfaces[0]
            .clone()
            .methods
            .into_iter()
            .any(|x| x.name == "~Interface"));
    }

    #[test]
    fn should_parse_template_class() {
        create_model(TEMPLATE_INTERFACE);
    }

    #[test]
    fn should_list_template_parameters() {
        let model = create_model(TEMPLATE_INTERFACE);
        assert!(model.interfaces.len() > 0);
        assert_eq!(model.interfaces[0]
                       .clone()
                       .template_parameters
                       .unwrap(),
                   vec![TemplateParameter { type_name: "T".to_string() },
                        TemplateParameter { type_name: "U".to_string() },
                        TemplateParameter { type_name: "V".to_string() }]);
    }

    #[test]
    fn should_generate_random_names_for_anonymous_method_arguments() {

        const INTERFACE_WITH_NAMELESS_ARGUMENT_METHODS: &'static str = r#"
            struct Foo {
                virtual void noArgumentName(double) = 0;
            };
            "#;

        let model = create_model(INTERFACE_WITH_NAMELESS_ARGUMENT_METHODS);
        assert!(serde_json::ser::to_string(&model.interfaces[0].methods[0].arguments[0].name)
            .unwrap()
            .len() > 0)
    }

    #[test]
    fn should_parse_template_methods() {

        const INTERFACE_WITH_TEMPLATE_METHODS: &'static str = r#"
            struct Foo {
                template <typename T> void foo(T bar);
            };
            "#;

        let model = create_model(INTERFACE_WITH_TEMPLATE_METHODS);
        assert_eq!(model.interfaces[0]
                           .clone()
                           .methods[0]
                       .clone()
                       .template_arguments
                       .unwrap(),
                   vec![TemplateParameter { type_name: "T".to_string() }])
    }

    #[test]
    fn should_parse_arguments_with_namespaced_types() {
        const INTERFACE_WITH_NAMESPACED_METHOD_PARAMETERS: &'static str = r#"
           namespace a { namespace b {
             using c = int;
           }}

           struct A {
               virtual void method(a::b::c abc);
           };
           "#;

        let model = create_model(INTERFACE_WITH_NAMESPACED_METHOD_PARAMETERS);
        println!("{:?}", model);
        assert_eq!(model
                   .interfaces[0]
                   .clone()
                   .methods[0]
                   .clone()
                   .arguments[0]
                   .argument_type
                , "a::b::c".to_string());
        assert_eq!(model
                   .interfaces[0]
                   .clone()
                   .methods[0]
                   .clone()
                   .arguments[0]
                   .name
                   , Some("abc".to_string()));
    }
}
<|endoftext|>