<commit_before>//! Developer note:
//!
//! This is a set of embed builders for rich embeds.
//!
//! These are used in the [`ChannelId::send_message`] and
//! [`ExecuteWebhook::embeds`] methods, both as part of builders.
//!
//! The only builder that should be exposed is [`CreateEmbed`]. The rest of
//! these have no real reason for being exposed, but are for completeness' sake.
//!
//! Documentation for embeds can be found [here].
//!
//! [`ChannelId::send_message`]: ../model/id/struct.ChannelId.html#method.send_message
//! [`CreateEmbed`]: struct.CreateEmbed.html
//! [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
//! [here]: https://discordapp.com/developers/docs/resources/channel#embed-object

use chrono::{DateTime, TimeZone};
use internal::prelude::*;
use model::channel::Embed;
use serde_json::Value;
use std::default::Default;
use std::fmt::Display;
use utils;
use utils::VecMap;

#[cfg(feature = "utils")]
use utils::Colour;

/// A builder to create a fake [`Embed`] object, for use with the
/// [`ChannelId::send_message`] and [`ExecuteWebhook::embeds`] methods.
///
/// # Examples
///
/// Refer to the documentation for [`ChannelId::send_message`] for a very in-depth
/// example on how to use this.
///
/// [`ChannelId::send_message`]: ../model/id/struct.ChannelId.html#method.send_message
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub VecMap<&'static str, Value>);

impl CreateEmbed {
    /// Set the author of the embed.
    ///
    /// Refer to the documentation for [`CreateEmbedAuthor`] for more
    /// information.
    ///
    /// [`CreateEmbedAuthor`]: struct.CreateEmbedAuthor.html
    pub fn author<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedAuthor) -> CreateEmbedAuthor {
        let map = utils::vecmap_to_json_map(f(CreateEmbedAuthor::default()).0);

        self.0.insert("author", Value::Object(map));

        CreateEmbed(self.0)
    }

    /// Set the colour of the left-hand side of the embed.
    ///
    /// This is an alias of [`colour`].
    ///
    /// [`colour`]: #method.colour
    #[cfg(feature = "utils")]
    #[inline]
    pub fn color<C: Into<Colour>>(self, colour: C) -> Self { self.colour(colour.into()) }

    /// Set the colour of the left-hand side of the embed.
    #[cfg(feature = "utils")]
    pub fn colour<C: Into<Colour>>(mut self, colour: C) -> Self {
        self.0.insert(
            "color",
            Value::Number(Number::from(u64::from(colour.into().0))),
        );

        CreateEmbed(self.0)
    }

    /// Set the colour of the left-hand side of the embed.
    ///
    /// This is an alias of [`colour`].
    ///
    /// [`colour`]: #method.colour
    #[cfg(not(feature = "utils"))]
    #[inline]
    pub fn color(self, colour: u32) -> Self { self.colour(colour) }

    /// Set the colour of the left-hand side of the embed.
    #[cfg(not(feature = "utils"))]
    pub fn colour(mut self, colour: u32) -> Self {
        self.0
            .insert("color", Value::Number(Number::from(colour)));

        CreateEmbed(self.0)
    }

    /// Set the description of the embed.
    ///
    /// **Note**: This can't be longer than 2048 characters.
    pub fn description<D: Display>(mut self, description: D) -> Self {
        self.0.insert(
            "description",
            Value::String(description.to_string()),
        );

        CreateEmbed(self.0)
    }

    /// Set a field. Note that this will not overwrite other fields, and will
    /// add to them.
    ///
    /// Refer to the documentation for [`CreateEmbedField`] for more
    /// information.
    ///
    /// **Note**: Maximum amount of characters you can put is 256 in a field
    /// name and 1024 in a field value and a field is inline by default.
    ///
    /// [`CreateEmbedField`]: struct.CreateEmbedField.html
    pub fn field<T, U>(mut self, name: T, value: U, inline: bool) -> Self
        where T: Display, U: Display {
        {
            let entry = self.0
                .entry("fields")
                .or_insert_with(|| Value::Array(vec![]));

            if let Value::Array(ref mut inner) = *entry {
                inner.push(json!({
                    "inline": inline,
                    "name": name.to_string(),
                    "value": value.to_string(),
                }));
            }
        }

        self
    }

    /// Adds multiple fields at once.
    pub fn fields<T, U, It>(mut self, fields: It) -> Self
        where It: IntoIterator<Item=(T, U, bool)>,
              T: Display,
              U: Display {
        for field in fields {
            self = self.field(field.0.to_string(), field.1.to_string(), field.2);
        }

        self
    }

    /// Set the footer of the embed.
    ///
    /// Refer to the documentation for [`CreateEmbedFooter`] for more
    /// information.
    ///
    /// [`CreateEmbedFooter`]: struct.CreateEmbedFooter.html
    pub fn footer<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedFooter) -> CreateEmbedFooter {
        let footer = f(CreateEmbedFooter::default()).0;
        let map = utils::vecmap_to_json_map(footer);

        self.0.insert("footer", Value::Object(map));

        CreateEmbed(self.0)
    }

    fn url_object(mut self, name: &'static str, url: &str) -> Self {
        let obj = json!({
            "url": url.to_string()
        });

        self.0.insert(name, obj);

        CreateEmbed(self.0)
    }

    /// Set the image associated with the embed. This only supports HTTP(S).
    pub fn image(self, url: &str) -> Self {
        self.url_object("image", url)
    }

    /// Set the video associated with the embed. This only supports HTTP(S).
    pub fn video(self, url: &str) -> Self {
        self.url_object("video", url)
    }

    /// Set the thumbnail of the embed. This only supports HTTP(S).
    pub fn thumbnail(self, url: &str) -> Self {
        self.url_object("thumbnail", url)
    }

    /// Set the timestamp.
    ///
    /// You may pass a direct string:
    ///
    /// - `2017-01-03T23:00:00`
    /// - `2004-06-08T16:04:23`
    /// - `2004-06-08T16:04:23`
    ///
    /// This timestamp must be in ISO-8601 format. It must also be in UTC format.
    ///
    /// You can also pass anything that implements `chrono::TimeZone`.
    ///
    /// # Examples
    ///
    /// Passing a string timestamp:
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::channel::Message;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler {
    ///     fn message(&self, _: Context, msg: Message) {
    ///         if msg.content == "~embed" {
    ///             let _ = msg.channel_id.send_message(|m| m
    ///              .embed(|e| e
    ///                     .title("hello")
    ///                     .timestamp("2004-06-08T16:04:23")));
    ///         }
    ///     }
    /// }
    ///
    /// let mut client = Client::new("token", Handler).unwrap();
    ///
    /// client.start().unwrap();
    /// ```
    ///
    /// Creating a join-log:
    ///
    /// Note: this example isn't efficient and is for demonstrative purposes.
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::guild::Member;
    /// use serenity::model::id::GuildId;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler {
    ///     fn guild_member_addition(&self, _: Context, guild_id: GuildId, member: Member) {
    ///         use serenity::CACHE;
    ///         let cache = CACHE.read();
    ///
    ///         if let Some(guild) = cache.guild(guild_id) {
    ///             let guild = guild.read();
    ///
    ///             let channel_search = guild
    ///                 .channels
    ///                 .values()
    ///                 .find(|c| c.read().name == "join-log");
    ///
    ///             if let Some(channel) = channel_search {
    ///                 let user = member.user.read();
    ///
    ///                 let _ = channel.read().send_message(|m| m
    ///                     .embed(|e| {
    ///                         let mut e = e
    ///                             .author(|a| a.icon_url(&user.face()).name(&user.name))
    ///                             .title("Member Join");
    ///
    ///                         if let Some(ref joined_at) = member.joined_at {
    ///                             e = e.timestamp(joined_at);
    ///                         }
    ///
    ///                         e
    ///                     }));
    ///             }
    ///         }
    ///     }
    /// }
    ///
    /// let mut client = Client::new("token", Handler).unwrap();
    ///
    /// client.start().unwrap();
    /// ```
    pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
        self.0
            .insert("timestamp", Value::String(timestamp.into().ts));

        CreateEmbed(self.0)
    }

    /// Set the title of the embed.
    pub fn title<D: Display>(mut self, title: D) -> Self {
        self.0
            .insert("title", Value::String(title.to_string()));

        CreateEmbed(self.0)
    }

    /// Set the URL to direct to when clicking on the title.
    pub fn url(mut self, url: &str) -> Self {
        self.0
            .insert("url", Value::String(url.to_string()));

        CreateEmbed(self.0)
    }

    /// Same as calling [`image`] with "attachment://filename.(jpg, png)".
    ///
    /// Note however, you have to be sure you set an attachment (with [`ChannelId::send_files`])
    /// with the provided filename. Or else this won't work.
    ///
    /// [`ChannelId::send_files`]: ../model/id/struct.ChannelId.html#send_files
    pub fn attachment(self, filename: &str) -> Self {
        self.image(&format!("attachment://{}", filename))
    }
}

impl Default for CreateEmbed {
    /// Creates a builder with default values, setting the `type` to `rich`.
    fn default() -> CreateEmbed {
        let mut map = VecMap::new();
        map.insert("type", Value::String("rich".to_string()));

        CreateEmbed(map)
    }
}

impl From<Embed> for CreateEmbed {
    /// Converts the fields of an embed into the values for a new embed builder.
    ///
    /// Some values - such as Proxy URLs - are not preserved.
    fn from(embed: Embed) -> CreateEmbed {
        let mut b = CreateEmbed::default().colour(embed.colour);

        if let Some(author) = embed.author {
            b = b.author(move |mut a| {
                a = a.name(&author.name);

                if let Some(icon_url) = author.icon_url {
                    a = a.icon_url(&icon_url);
                }

                if let Some(url) = author.url {
                    a = a.url(&url);
                }

                a
            });
        }

        if let Some(description) = embed.description {
            b = b.description(&description);
        }

        for field in embed.fields {
            b = b.field(field.name, field.value, field.inline);
        }

        if let Some(image) = embed.image {
            b = b.image(&image.url);
        }

        if let Some(timestamp) = embed.timestamp {
            b = b.timestamp(timestamp);
        }

        if let Some(thumbnail) = embed.thumbnail {
            b = b.thumbnail(&thumbnail.url);
        }

        if let Some(url) = embed.url {
            b = b.url(&url);
        }

        if let Some(title) = embed.title {
            b = b.title(&title);
        }

        if let Some(footer) = embed.footer {
            b = b.footer(move |mut f| {
                f = f.text(&footer.text);

                if let Some(icon_url) = footer.icon_url {
                    f = f.icon_url(&icon_url);
                }

                f
            });
        }

        b
    }
}

/// A builder to create a fake [`Embed`] object's author, for use with the
/// [`CreateEmbed::author`] method.
///
/// Requires that you specify a [`name`].
///
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub VecMap<&'static str, Value>);

impl CreateEmbedAuthor {
    /// Set the URL of the author's icon.
    pub fn icon_url(mut self, icon_url: &str) -> Self {
        self.0.insert("icon_url", Value::String(icon_url.to_string()));

        self
    }

    /// Set the author's name.
    pub fn name(mut self, name: &str) -> Self {
        self.0.insert("name", Value::String(name.to_string()));

        self
    }

    /// Set the author's URL.
    pub fn url(mut self, url: &str) -> Self {
        self.0.insert("url", Value::String(url.to_string()));

        self
    }
}

/// A builder to create a fake [`Embed`] object's footer, for use with the
/// [`CreateEmbed::footer`] method.
///
/// This does not require any field be set.
///
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub VecMap<&'static str, Value>);

impl CreateEmbedFooter {
    /// Set the icon URL's value. This only supports HTTP(S).
    pub fn icon_url(mut self, icon_url: &str) -> Self {
        self.0.insert("icon_url", Value::String(icon_url.to_string()));

        self
    }

    /// Set the footer's text.
    pub fn text<D: Display>(mut self, text: D) -> Self {
        self.0.insert("text", Value::String(text.to_string()));

        self
    }
}

#[derive(Clone, Debug)]
pub struct Timestamp {
    pub ts: String,
}

impl From<String> for Timestamp {
    fn from(ts: String) -> Self {
        Timestamp {
            ts: ts,
        }
    }
}

impl<'a> From<&'a str> for Timestamp {
    fn from(ts: &'a str) -> Self {
        Timestamp {
            ts: ts.to_string(),
        }
    }
}

impl<'a, Tz: TimeZone> From<&'a DateTime<Tz>> for Timestamp
    where Tz::Offset: Display {
    fn from(dt: &'a DateTime<Tz>) -> Self {
        Timestamp {
            ts: dt.to_rfc3339(),
        }
    }
}
<commit_msg>Inline simple url properties<commit_after>//! Developer note:
//!
//! This is a set of embed builders for rich embeds.
//!
//! These are used in the [`ChannelId::send_message`] and
//! [`ExecuteWebhook::embeds`] methods, both as part of builders.
//!
//! The only builder that should be exposed is [`CreateEmbed`]. The rest of
//! these have no real reason for being exposed, but are for completeness' sake.
//!
//! Documentation for embeds can be found [here].
//!
//! [`ChannelId::send_message`]: ../model/id/struct.ChannelId.html#method.send_message
//! [`CreateEmbed`]: struct.CreateEmbed.html
//! [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
//! [here]: https://discordapp.com/developers/docs/resources/channel#embed-object

use chrono::{DateTime, TimeZone};
use internal::prelude::*;
use model::channel::Embed;
use serde_json::Value;
use std::default::Default;
use std::fmt::Display;
use utils;
use utils::VecMap;

#[cfg(feature = "utils")]
use utils::Colour;

/// A builder to create a fake [`Embed`] object, for use with the
/// [`ChannelId::send_message`] and [`ExecuteWebhook::embeds`] methods.
///
/// # Examples
///
/// Refer to the documentation for [`ChannelId::send_message`] for a very in-depth
/// example on how to use this.
///
/// [`ChannelId::send_message`]: ../model/id/struct.ChannelId.html#method.send_message
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub VecMap<&'static str, Value>);

impl CreateEmbed {
    /// Set the author of the embed.
    ///
    /// Refer to the documentation for [`CreateEmbedAuthor`] for more
    /// information.
    ///
    /// [`CreateEmbedAuthor`]: struct.CreateEmbedAuthor.html
    pub fn author<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedAuthor) -> CreateEmbedAuthor {
        let map = utils::vecmap_to_json_map(f(CreateEmbedAuthor::default()).0);

        self.0.insert("author", Value::Object(map));

        CreateEmbed(self.0)
    }

    /// Set the colour of the left-hand side of the embed.
    ///
    /// This is an alias of [`colour`].
    ///
    /// [`colour`]: #method.colour
    #[cfg(feature = "utils")]
    #[inline]
    pub fn color<C: Into<Colour>>(self, colour: C) -> Self { self.colour(colour.into()) }

    /// Set the colour of the left-hand side of the embed.
    #[cfg(feature = "utils")]
    pub fn colour<C: Into<Colour>>(mut self, colour: C) -> Self {
        self.0.insert(
            "color",
            Value::Number(Number::from(u64::from(colour.into().0))),
        );

        CreateEmbed(self.0)
    }

    /// Set the colour of the left-hand side of the embed.
    ///
    /// This is an alias of [`colour`].
    ///
    /// [`colour`]: #method.colour
    #[cfg(not(feature = "utils"))]
    #[inline]
    pub fn color(self, colour: u32) -> Self { self.colour(colour) }

    /// Set the colour of the left-hand side of the embed.
    #[cfg(not(feature = "utils"))]
    pub fn colour(mut self, colour: u32) -> Self {
        self.0
            .insert("color", Value::Number(Number::from(colour)));

        CreateEmbed(self.0)
    }

    /// Set the description of the embed.
    ///
    /// **Note**: This can't be longer than 2048 characters.
    pub fn description<D: Display>(mut self, description: D) -> Self {
        self.0.insert(
            "description",
            Value::String(description.to_string()),
        );

        CreateEmbed(self.0)
    }

    /// Set a field. Note that this will not overwrite other fields, and will
    /// add to them.
    ///
    /// Refer to the documentation for [`CreateEmbedField`] for more
    /// information.
    ///
    /// **Note**: Maximum amount of characters you can put is 256 in a field
    /// name and 1024 in a field value and a field is inline by default.
    ///
    /// [`CreateEmbedField`]: struct.CreateEmbedField.html
    pub fn field<T, U>(mut self, name: T, value: U, inline: bool) -> Self
        where T: Display, U: Display {
        {
            let entry = self.0
                .entry("fields")
                .or_insert_with(|| Value::Array(vec![]));

            if let Value::Array(ref mut inner) = *entry {
                inner.push(json!({
                    "inline": inline,
                    "name": name.to_string(),
                    "value": value.to_string(),
                }));
            }
        }

        self
    }

    /// Adds multiple fields at once.
    pub fn fields<T, U, It>(mut self, fields: It) -> Self
        where It: IntoIterator<Item=(T, U, bool)>,
              T: Display,
              U: Display {
        for field in fields {
            self = self.field(field.0.to_string(), field.1.to_string(), field.2);
        }

        self
    }

    /// Set the footer of the embed.
    ///
    /// Refer to the documentation for [`CreateEmbedFooter`] for more
    /// information.
    ///
    /// [`CreateEmbedFooter`]: struct.CreateEmbedFooter.html
    pub fn footer<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedFooter) -> CreateEmbedFooter {
        let footer = f(CreateEmbedFooter::default()).0;
        let map = utils::vecmap_to_json_map(footer);

        self.0.insert("footer", Value::Object(map));

        CreateEmbed(self.0)
    }

    fn url_object(mut self, name: &'static str, url: &str) -> Self {
        let obj = json!({
            "url": url.to_string()
        });

        self.0.insert(name, obj);

        CreateEmbed(self.0)
    }

    /// Set the image associated with the embed. This only supports HTTP(S).
    #[inline]
    pub fn image(self, url: &str) -> Self {
        self.url_object("image", url)
    }

    /// Set the video associated with the embed. This only supports HTTP(S).
    #[inline]
    pub fn video(self, url: &str) -> Self {
        self.url_object("video", url)
    }

    /// Set the thumbnail of the embed. This only supports HTTP(S).
    #[inline]
    pub fn thumbnail(self, url: &str) -> Self {
        self.url_object("thumbnail", url)
    }

    /// Set the timestamp.
    ///
    /// You may pass a direct string:
    ///
    /// - `2017-01-03T23:00:00`
    /// - `2004-06-08T16:04:23`
    /// - `2004-06-08T16:04:23`
    ///
    /// This timestamp must be in ISO-8601 format. It must also be in UTC format.
    ///
    /// You can also pass anything that implements `chrono::TimeZone`.
    ///
    /// # Examples
    ///
    /// Passing a string timestamp:
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::channel::Message;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler {
    ///     fn message(&self, _: Context, msg: Message) {
    ///         if msg.content == "~embed" {
    ///             let _ = msg.channel_id.send_message(|m| m
    ///              .embed(|e| e
    ///                     .title("hello")
    ///                     .timestamp("2004-06-08T16:04:23")));
    ///         }
    ///     }
    /// }
    ///
    /// let mut client = Client::new("token", Handler).unwrap();
    ///
    /// client.start().unwrap();
    /// ```
    ///
    /// Creating a join-log:
    ///
    /// Note: this example isn't efficient and is for demonstrative purposes.
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::guild::Member;
    /// use serenity::model::id::GuildId;
    ///
    /// struct Handler;
    ///
    /// impl EventHandler for Handler {
    ///     fn guild_member_addition(&self, _: Context, guild_id: GuildId, member: Member) {
    ///         use serenity::CACHE;
    ///         let cache = CACHE.read();
    ///
    ///         if let Some(guild) = cache.guild(guild_id) {
    ///             let guild = guild.read();
    ///
    ///             let channel_search = guild
    ///                 .channels
    ///                 .values()
    ///                 .find(|c| c.read().name == "join-log");
    ///
    ///             if let Some(channel) = channel_search {
    ///                 let user = member.user.read();
    ///
    ///                 let _ = channel.read().send_message(|m| m
    ///                     .embed(|e| {
    ///                         let mut e = e
    ///                             .author(|a| a.icon_url(&user.face()).name(&user.name))
    ///                             .title("Member Join");
    ///
    ///                         if let Some(ref joined_at) = member.joined_at {
    ///                             e = e.timestamp(joined_at);
    ///                         }
    ///
    ///                         e
    ///                     }));
    ///             }
    ///         }
    ///     }
    /// }
    ///
    /// let mut client = Client::new("token", Handler).unwrap();
    ///
    /// client.start().unwrap();
    /// ```
    pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
        self.0
            .insert("timestamp", Value::String(timestamp.into().ts));

        CreateEmbed(self.0)
    }

    /// Set the title of the embed.
    pub fn title<D: Display>(mut self, title: D) -> Self {
        self.0
            .insert("title", Value::String(title.to_string()));

        CreateEmbed(self.0)
    }

    /// Set the URL to direct to when clicking on the title.
    pub fn url(mut self, url: &str) -> Self {
        self.0
            .insert("url", Value::String(url.to_string()));

        CreateEmbed(self.0)
    }

    /// Same as calling [`image`] with "attachment://filename.(jpg, png)".
    ///
    /// Note however, you have to be sure you set an attachment (with [`ChannelId::send_files`])
    /// with the provided filename. Or else this won't work.
    ///
    /// [`ChannelId::send_files`]: ../model/id/struct.ChannelId.html#send_files
    pub fn attachment(self, filename: &str) -> Self {
        self.image(&format!("attachment://{}", filename))
    }
}

impl Default for CreateEmbed {
    /// Creates a builder with default values, setting the `type` to `rich`.
    fn default() -> CreateEmbed {
        let mut map = VecMap::new();
        map.insert("type", Value::String("rich".to_string()));

        CreateEmbed(map)
    }
}

impl From<Embed> for CreateEmbed {
    /// Converts the fields of an embed into the values for a new embed builder.
    ///
    /// Some values - such as Proxy URLs - are not preserved.
    fn from(embed: Embed) -> CreateEmbed {
        let mut b = CreateEmbed::default().colour(embed.colour);

        if let Some(author) = embed.author {
            b = b.author(move |mut a| {
                a = a.name(&author.name);

                if let Some(icon_url) = author.icon_url {
                    a = a.icon_url(&icon_url);
                }

                if let Some(url) = author.url {
                    a = a.url(&url);
                }

                a
            });
        }

        if let Some(description) = embed.description {
            b = b.description(&description);
        }

        for field in embed.fields {
            b = b.field(field.name, field.value, field.inline);
        }

        if let Some(image) = embed.image {
            b = b.image(&image.url);
        }

        if let Some(timestamp) = embed.timestamp {
            b = b.timestamp(timestamp);
        }

        if let Some(thumbnail) = embed.thumbnail {
            b = b.thumbnail(&thumbnail.url);
        }

        if let Some(url) = embed.url {
            b = b.url(&url);
        }

        if let Some(title) = embed.title {
            b = b.title(&title);
        }

        if let Some(footer) = embed.footer {
            b = b.footer(move |mut f| {
                f = f.text(&footer.text);

                if let Some(icon_url) = footer.icon_url {
                    f = f.icon_url(&icon_url);
                }

                f
            });
        }

        b
    }
}

/// A builder to create a fake [`Embed`] object's author, for use with the
/// [`CreateEmbed::author`] method.
///
/// Requires that you specify a [`name`].
///
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub VecMap<&'static str, Value>);

impl CreateEmbedAuthor {
    /// Set the URL of the author's icon.
    pub fn icon_url(mut self, icon_url: &str) -> Self {
        self.0.insert("icon_url", Value::String(icon_url.to_string()));

        self
    }

    /// Set the author's name.
    pub fn name(mut self, name: &str) -> Self {
        self.0.insert("name", Value::String(name.to_string()));

        self
    }

    /// Set the author's URL.
    pub fn url(mut self, url: &str) -> Self {
        self.0.insert("url", Value::String(url.to_string()));

        self
    }
}

/// A builder to create a fake [`Embed`] object's footer, for use with the
/// [`CreateEmbed::footer`] method.
///
/// This does not require any field be set.
///
/// [`Embed`]: ../model/channel/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub VecMap<&'static str, Value>);

impl CreateEmbedFooter {
    /// Set the icon URL's value. This only supports HTTP(S).
    pub fn icon_url(mut self, icon_url: &str) -> Self {
        self.0.insert("icon_url", Value::String(icon_url.to_string()));

        self
    }

    /// Set the footer's text.
    pub fn text<D: Display>(mut self, text: D) -> Self {
        self.0.insert("text", Value::String(text.to_string()));

        self
    }
}

#[derive(Clone, Debug)]
pub struct Timestamp {
    pub ts: String,
}

impl From<String> for Timestamp {
    fn from(ts: String) -> Self {
        Timestamp {
            ts: ts,
        }
    }
}

impl<'a> From<&'a str> for Timestamp {
    fn from(ts: &'a str) -> Self {
        Timestamp {
            ts: ts.to_string(),
        }
    }
}

impl<'a, Tz: TimeZone> From<&'a DateTime<Tz>> for Timestamp
    where Tz::Offset: Display {
    fn from(dt: &'a DateTime<Tz>) -> Self {
        Timestamp {
            ts: dt.to_rfc3339(),
        }
    }
}
<|endoftext|>


<commit_before>
#![feature(test)]

extern crate test;
use test::Bencher;

#[macro_use(s)]
extern crate ndarray;
use ndarray::prelude::*;

const N: usize = 1024;
const X: usize = 64;
const Y: usize = 16;

#[bench]
fn map_regular(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N).into_shape((X, Y)).unwrap();
    bench.iter(|| {
        a.map(|&x| 2. * x)
    });
}

#[bench]
fn map_stride_f64(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap();
    let av = a.slice(s![.., ..;2]);
    bench.iter(|| {
        av.map(|&x| 2. * x)
    });
}

#[bench]
fn map_stride_u32(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap();
    let b = a.mapv(|x| x as u32);
    let av = b.slice(s![.., ..;2]);
    bench.iter(|| {
        av.map(|&x| 2 * x)
    });
}
<commit_msg>Add another bench in higher-order<commit_after>
#![feature(test)]

extern crate test;
use test::Bencher;

#[macro_use(s)]
extern crate ndarray;
use ndarray::prelude::*;
use ndarray::ArrayViewMut2;

const N: usize = 1024;
const X: usize = 64;
const Y: usize = 16;

#[bench]
fn map_regular(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N).into_shape((X, Y)).unwrap();
    bench.iter(|| {
        a.map(|&x| 2. * x)
    });
}

pub fn double_array(mut a: ArrayViewMut2<f64>) {
    a *= 2.0;
}

#[bench]
fn map_stride_double_f64(bench: &mut Bencher)
{
    let mut a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap();
    let mut av = a.slice_mut(s![.., ..;2]);
    bench.iter(|| {
        double_array(av.view_mut());

    });
}

#[bench]
fn map_stride_f64(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap();
    let av = a.slice(s![.., ..;2]);
    bench.iter(|| {
        av.map(|&x| 2. * x)
    });
}

#[bench]
fn map_stride_u32(bench: &mut Bencher)
{
    let a = Array::linspace(0., 127., N * 2).into_shape((X, Y * 2)).unwrap();
    let b = a.mapv(|x| x as u32);
    let av = b.slice(s![.., ..;2]);
    bench.iter(|| {
        av.map(|&x| 2 * x)
    });
}
<|endoftext|>


<commit_before>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {

            debug!("Processing list item {:#?}", part);

            match part {
                v @ Expression::Value(OperatorOrToken::Token(TokenOffset {
                    token: Token::Number { .. }, ..
                })) => {
                    if self.last_was_an_operator {
                        debug!("Push on value stack {:#?}", v);
                        self.value_stack.push(v);
                    } else {
                        debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                        if self.paren_level > 0 {
                            while !self.op_stack.is_empty() &&
                                  self.op_stack.last().unwrap().operator !=
                                      Operator::LeftParen {

                                debug!("op stack last {:#?}", self.op_stack.last());
                                self.math_machine();
                            }
                        }

                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            v,
                        );

                        debug!("Push on list on value stack {:#?}", list);

                        self.value_stack.push(list);
                    }

                    self.last_was_an_operator = false;
                },
                Expression::Value(OperatorOrToken::Operator(OperatorOffset {
                    operator: Operator::RightParen, ..
                })) => {
                    debug!("RIGHT PAREN");
                    debug!("op stack = {:#?}", self.op_stack);

                    let mut last_operator = self.op_stack.pop();
                    while last_operator.is_some() &&
                          last_operator.unwrap().operator !=
                              Operator::LeftParen {

                        debug!("last operator before right paren = {:#?}", last_operator);
                        self.op_stack.push(last_operator.unwrap());
                        self.math_machine();
                        last_operator = self.op_stack.pop();
                    }
                    self.last_was_an_operator = false;
                    self.paren_level -= 1;
                },
                Expression::Value(OperatorOrToken::Operator(OperatorOffset {
                    operator: Operator::LeftParen, offset
                })) => {
                    debug!("Push on op stack Leftparen");
                    self.op_stack.push(OperatorOffset {
                        operator: Operator::LeftParen, offset: offset
                    });
                    self.last_was_an_operator = true;
                    self.paren_level += 1;
                },
                Expression::Value(OperatorOrToken::Operator(
                    oo @ OperatorOffset { .. }
                )) => {
                    if let Some(&last_operator) = self.op_stack.last() {
                        if last_operator.operator
                                .same_or_greater_precedence(oo.operator) {
                            self.math_machine();
                        }
                    }
                    debug!("Push on op stack {:#?}", oo);
                    self.op_stack.push(oo);
                    self.last_was_an_operator = true;
                },
                Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                    token: Token::String(_), ..
                })) => {
                    let var_eval = self.context.get_variable(&t)
                                    .unwrap_or(Expression::Value(
                                        OperatorOrToken::Token(t)
                                    ));

                    if self.last_was_an_operator {
                        self.value_stack.push(var_eval);
                    } else {
                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            var_eval,
                        );
                        self.value_stack.push(list);
                    }
                    self.last_was_an_operator = false;
                },
                Expression::List(list) => {
                    debug!("Push list on value stack {:#?}", list);
                    self.value_stack.push(Expression::List(list));
                    self.last_was_an_operator = false;
                },
                other => {
                    if self.last_was_an_operator {
                        self.value_stack.push(other);
                    } else {
                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            other,
                        );
                        self.value_stack.push(list);
                    }
                    self.last_was_an_operator = false;
                },
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);

        let second = self.value_stack.pop()
                       .expect("Expected a second argument on the value stack");
        debug!("second = {:#?}", second);

        let first = self.value_stack.pop()
                       .expect("Expected a first argument on the value stack");
        debug!("first: {:#?}", first);

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<commit_msg>Extract fns for popping ops and vals from their stacks<commit_after>use token::Token;
use context::Context;
use operator::Operator;
use operator_offset::OperatorOffset;
use operator_or_token::OperatorOrToken;
use token_offset::TokenOffset;
use ast::expression::Expression;

pub struct ExpressionEvaluator<'a> {
    context: &'a Context,
    pub paren_level: i32,
    last_was_an_operator: bool,
    value_stack: Vec<Expression>,
    op_stack: Vec<OperatorOffset>,
}

impl<'a> ExpressionEvaluator<'a> {
    pub fn evaluate(expr: Expression, context: &Context) -> Expression {
        match expr {
            Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                token: Token::String(_), ..
            })) => {
                context.get_variable(&t)
                       .unwrap_or(Expression::Value(
                            OperatorOrToken::Token(t)
                       ))
            },
            Expression::List(exprs) => {
                let evaluator = ExpressionEvaluator::new(context);
                evaluator.evaluate_list(exprs)
            },
            other => other,
        }
    }

    pub fn new(context: &Context) -> ExpressionEvaluator {
        ExpressionEvaluator {
            context: context,
            paren_level: 0,
            last_was_an_operator: true,
            value_stack: Vec::new(),
            op_stack: Vec::new(),
        }
    }

    pub fn evaluate_list(mut self, exprs: Vec<Expression>) -> Expression {

        // Split into value stacks and operator stacks
        let mut exprs = exprs.into_iter();

        while let Some(part) = exprs.next() {

            debug!("Processing list item {:#?}", part);

            match part {
                v @ Expression::Value(OperatorOrToken::Token(TokenOffset {
                    token: Token::Number { .. }, ..
                })) => {
                    if self.last_was_an_operator {
                        debug!("Push on value stack {:#?}", v);
                        self.value_stack.push(v);
                    } else {
                        debug!("Number, last_was_an_operator=false, paren_level={}", self.paren_level);

                        if self.paren_level > 0 {
                            while !self.op_stack.is_empty() &&
                                  self.op_stack.last().unwrap().operator !=
                                      Operator::LeftParen {

                                debug!("op stack last {:#?}", self.op_stack.last());
                                self.math_machine();
                            }
                        }

                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            v,
                        );

                        debug!("Push on list on value stack {:#?}", list);

                        self.value_stack.push(list);
                    }

                    self.last_was_an_operator = false;
                },
                Expression::Value(OperatorOrToken::Operator(OperatorOffset {
                    operator: Operator::RightParen, ..
                })) => {
                    debug!("RIGHT PAREN");
                    debug!("op stack = {:#?}", self.op_stack);

                    let mut last_operator = self.op_stack.pop();
                    while last_operator.is_some() &&
                          last_operator.unwrap().operator !=
                              Operator::LeftParen {

                        debug!("last operator before right paren = {:#?}", last_operator);
                        self.op_stack.push(last_operator.unwrap());
                        self.math_machine();
                        last_operator = self.op_stack.pop();
                    }
                    self.last_was_an_operator = false;
                    self.paren_level -= 1;
                },
                Expression::Value(OperatorOrToken::Operator(OperatorOffset {
                    operator: Operator::LeftParen, offset
                })) => {
                    debug!("Push on op stack Leftparen");
                    self.op_stack.push(OperatorOffset {
                        operator: Operator::LeftParen, offset: offset
                    });
                    self.last_was_an_operator = true;
                    self.paren_level += 1;
                },
                Expression::Value(OperatorOrToken::Operator(
                    oo @ OperatorOffset { .. }
                )) => {
                    if let Some(&last_operator) = self.op_stack.last() {
                        if last_operator.operator
                                .same_or_greater_precedence(oo.operator) {
                            self.math_machine();
                        }
                    }
                    debug!("Push on op stack {:#?}", oo);
                    self.op_stack.push(oo);
                    self.last_was_an_operator = true;
                },
                Expression::Value(OperatorOrToken::Token(t @ TokenOffset {
                    token: Token::String(_), ..
                })) => {
                    let var_eval = self.context.get_variable(&t)
                                    .unwrap_or(Expression::Value(
                                        OperatorOrToken::Token(t)
                                    ));

                    if self.last_was_an_operator {
                        self.value_stack.push(var_eval);
                    } else {
                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            var_eval,
                        );
                        self.value_stack.push(list);
                    }
                    self.last_was_an_operator = false;
                },
                Expression::List(list) => {
                    debug!("Push list on value stack {:#?}", list);
                    self.value_stack.push(Expression::List(list));
                    self.last_was_an_operator = false;
                },
                other => {
                    if self.last_was_an_operator {
                        self.value_stack.push(other);
                    } else {
                        let list = Expression::create_list(
                            self.value_stack.pop(),
                            other,
                        );
                        self.value_stack.push(list);
                    }
                    self.last_was_an_operator = false;
                },
            }
        }

        debug!("PROCESS THE STACKS!");
        debug!("Op stack = {:#?}", self.op_stack);
        debug!("Value stack = {:#?}", self.value_stack);

        // Process the stacks
        while !self.op_stack.is_empty() {
            self.math_machine()
        }

        self.value_stack.pop().unwrap()
    }

    fn math_machine(&mut self) {
        debug!("Math machine:");

        let op     = self.get_operator();
        let second = self.get_value();
        let first  = self.get_value();

        let math_result = Expression::apply_math(
            op, first, second, self.context, self.paren_level,
        );
        debug!("Math result: {:#?}", math_result);

        self.value_stack.push(math_result);
    }

    fn get_operator(&mut self) -> OperatorOffset {
        // Math machine only gets called while there are operators on
        // the operator stack, so unwrap should be fine.
        let op = self.op_stack.pop().unwrap();
        debug!("op = {:#?}", op);
        op
    }

    fn get_value(&mut self) -> Expression {
        // TODO: Turn this into a SassError
        let val = self.value_stack.pop()
                      .expect("Expected an argument on the value stack");
        debug!("val = {:#?}", val);
        val
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::Token;
    use token_offset::TokenOffset;
    use operator_or_token::OperatorOrToken;
    use operator::Operator;
    use operator_offset::OperatorOffset;
    use context::Context;
    use ast::expression::Expression;

    fn one() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 1.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn two() -> OperatorOrToken {
        OperatorOrToken::Token(
            TokenOffset {
                token: Token::Number {
                    value: 2.0, units: None, computed: false
                },
                offset: None
            }
        )
    }

    fn plus() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Plus, offset: None }
        )
    }

    fn slash() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::Slash, offset: None }
        )
    }

    fn right_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::RightParen, offset: None }
        )
    }

    fn left_paren() -> OperatorOrToken {
        OperatorOrToken::Operator(
            OperatorOffset { operator: Operator::LeftParen, offset: None }
        )
    }

    #[test]
    fn it_evaluates_a_list_adding_fractions() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(plus()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::Value(OperatorOrToken::Token(
                TokenOffset {
                    token: Token::Number {
                        value: 1.0,
                        units: None,
                        computed: true,
                    },
                    offset: None,
                }
            ))
        );
    }

    #[test]
    fn it_evaluates_a_list_with_division_and_string_concat() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(plus()),
            Expression::Value(left_paren()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(two()),
            Expression::Value(right_paren()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::String(String::from("10.5")),
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                )),
                Expression::Value(OperatorOrToken::Token(
                    TokenOffset {
                        token: Token::Number {
                            value: 2.0,
                            units: None,
                            computed: false,
                        },
                        offset: None,
                    }
                ))
            ])
        );
    }

    #[test]
    fn it_does_not_divide_list_with_only_slashes() {
        let ex = Expression::List(vec![
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(one()),
            Expression::Value(slash()),
            Expression::Value(two()),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ExpressionEvaluator::evaluate(ex, &fake_context),
            Expression::List(vec![
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(one()),
                Expression::Value(slash()),
                Expression::Value(two()),
            ])
        );
    }
}
<|endoftext|>


<commit_before>//use nalgebra::Vector2;
//use std::cmp::max;
use num::traits::Signed;
use super::integer_math::*;
use std::ops::{Add, Sub, Mul, Neg, AddAssign, SubAssign, MulAssign};
use array_ext::*;

pub type Time = i64;

pub trait Trajectory {
  type Coefficient: Vector;
}

pub trait ScalarTrajectory: Trajectory {
  /*fn next_time_ge (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {

  }
  fn next_time_gt (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    self.next_time_ge (now, target + Vector::one())
  }
  fn next_time_le (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    (-self).next_time_ge (now, -target)
  }
  fn next_time_lt (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    self.next_time_le (now, target - Vector::one())
  }*/
}

macro_rules! impl_binop {
  ([$($generic_parameters: tt)*], $Left:ty, $Right:ty, $Trait: ident, $method: ident, $TraitAssign: ident, $method_assign: ident, $self: ident, $other: ident, $owned_owned: expr, $owned_ref: expr, $ref_owned: expr, $ref_ref: expr, $assign_owned: expr, $assign_ref: expr,) => {

impl <$($generic_parameters)*> $Trait <$Right> for $Left {
  type Output = Self;
  fn $method ($self, $other: $Right)->Self {
    $owned_owned
  }
}

impl <'a, $($generic_parameters)*> $Trait <& 'a $Right> for $Left {
  type Output = Self;
  fn $method ($self, $other: & 'a $Right)->Self {
    $owned_ref
  }
}

impl <'a, $($generic_parameters)*> $Trait <$Right> for & 'a $Left {
  type Output = $Left;
  fn $method ($self, $other: $Right)->$Left {
    $ref_owned
  }
}

impl <'a, 'b, $($generic_parameters)*> $Trait <& 'b $Right> for & 'a $Left {
  type Output = $Left;
  fn $method ($self, $other: & 'b $Right)->$Left {
    $ref_ref
  }
}

impl <$($generic_parameters)*> $TraitAssign <$Right> for $Left {
  fn $method_assign (&mut $self, $other: $Right) {
    $assign_owned
  }
}

impl <'a, $($generic_parameters)*> $TraitAssign <& 'a $Right> for $Left {
  fn $method_assign (&mut $self, $other: & 'a $Right) {
    $assign_ref
  }
}

  }
}

macro_rules! impl_trajectory_add_sub {
  ($Trajectory:ident, $Trait: ident, $method: ident, $TraitAssign: ident, $method_assign: ident) => {

impl_binop! {
[T: Vector], $Trajectory <T>, $Trajectory <T>, $Trait, $method, $TraitAssign, $method_assign, self, other,
{
  self += other;
  self
},
{
  self += other;
  self
},
{
  self.clone() + other
},
{
  self.clone() + other.clone()
},
{
  if self.origin < other.origin {self.set_origin (other.origin).unwrap();}
  if other.origin < self.origin {other.set_origin (self.origin).unwrap();}
  for (mine, others) in self.terms.iter_mut().zip (other.terms.into_iter()) {
    mine.$method_assign (others);
  }
},
{
  let mut other = other;
  let mut other_clone;
  if self.origin < other.origin {self.set_origin (other.origin).unwrap();}
  if other.origin < self.origin {
    other_clone = other.clone();
    other_clone.set_origin (self.origin).unwrap();
    other = & other_clone;
  }
  for (mine, others) in self.terms.iter_mut().zip (other.terms.iter()) {
    mine.$method_assign (others);
  }
},
}

impl_binop! {
[T: Vector], $Trajectory <T>, T, $Trait, $method, $TraitAssign, $method_assign, self, other,
{
  self.terms[0].$method_assign (other);
  self
},
{
  self.terms[0].$method_assign (other);
  self
},
{
  self.clone() + other
},
{
  self.clone() + other
},
{
  self.terms[0].$method_assign (other);
},
{
  self.terms[0].$method_assign (other);
},
}

  }
}

macro_rules! impl_trajectory {
  ($Trajectory: ident, $degree: expr) => {

#[derive (Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, Default)]
pub struct $Trajectory <T> {
  origin: Time,
  terms: [T; $degree + 1],
}

impl <T: Vector> $Trajectory <T> {
  pub fn constant (value: T)->Self {
    let terms = [T::zero(); $degree + 1];
    terms [0] = value;
    $Trajectory {origin: 0, terms}
  }
  pub fn set_origin (&mut self, new_origin: Time)->Result <(), polynomial::Error <Time>> {
    polynomial::translate (&mut self.terms, new_origin - self.origin)?;
    self.origin = new_origin;
    Ok (())
  }
  pub fn term (&self, time: Time, which: usize)->T {
    self.terms.taylor_coefficient (time - self.origin, which)
  }
  pub fn set_term (&mut self, time: Time, which: usize, value: T) {
    self.set_origin (time) ;
    self.terms.0 [which] = value;
  }
  pub fn add_term (&mut self, time: Time, which: usize, value: T) {
    self.set_origin (time);
    self.terms.0 [which] += value;
  }
  pub fn value (&mut self, time: Time)->T {self.term (time, 0)}
  pub fn velocity (&mut self, time: Time)->T {self.term (time, 1)}
  pub fn set_value (&mut self, time: Time, value: T) {self.set_term (time, 0, value)}
  pub fn set_velocity (&mut self, time: Time, value: T) {self.set_term (time, 1, value)}
  pub fn add_value (&mut self, value: T) {self += value}
  pub fn add_velocity (&mut self, time: Time, value: T) {self.add_term (time, 1, value)}
}

impl <T: Integer + Signed> $Trajectory <T> {
  pub fn next_time_lt (&self, now: Time, value: T)->Option <Time> {
    if self.value (now) < value {Some (now)}
    else {
      unimplemented!()
    }
  }
  pub fn next_time_le (&self, now: Time, value: T)->Option <Time> {
    self.next_time_lt (now, value + T::one())
  }
  pub fn next_time_gt (&self, now: Time, value: T)->Option <Time> {
    (-self).next_time_lt (now, -value)
  }
  pub fn next_time_ge (&self, now: Time, value: T)->Option <Time> {
    self.next_time_gt (now, value - T::one())
  }
}

impl_trajectory_add_sub! ($Trajectory, Add, add, AddAssign, add_assign);
impl_trajectory_add_sub! ($Trajectory, Sub, sub, SubAssign, sub_assign);

impl<T: Vector, Coordinate> Mul<Coordinate> for $Trajectory<T> where T: MulAssign <Coordinate> {
  type Output = Self;
  fn mul(self, other: Coordinate) -> Self {
    self *= other; self
  }
}

impl<'a, T: Vector, Coordinate> Mul<Coordinate> for & 'a $Trajectory<T> where T: MulAssign <Coordinate> {
  type Output = $Trajectory <T>;
  fn mul(self, other: Coordinate) ->$Trajectory <T> {
    self.clone()*other
  }
}

impl<T: Vector, Coordinate> MulAssign <Coordinate> for $Trajectory<T> where T: MulAssign <Coordinate> {
  fn mul_assign (&mut self, other: Coordinate) {
    for term in self.terms.iter_mut (){MulAssign::<Coordinate>::mul_assign (term, other);}
  }
}

/*impl_binop! {
[T: Vector], $Trajectory <T>, T::Coordinate, Mul, mul, MulAssign, mul_assign, self, other,
{
  self *= other;
  self
},
{
  self *= other;
  self
},
{
  self.clone()*other
},
{
  self.clone()*other
},
{
  for term in self.terms.iter_mut (){*term *= other;}
},
{
  for term in self.terms.iter_mut (){*term *= other;}
},
}*/

impl <T: Vector + Neg <Output = T>> Neg for $Trajectory <T> {
  type Output = Self;
  fn neg (self)->Self {
    $Trajectory {
      origin: self.origin,
      terms: Array::from_fn (| index | self.terms [index].clone().neg()),
    }
  }
}
impl <'a, T: Vector> Neg for & 'a $Trajectory <T> where & 'a T: Neg <Output = T> {
  type Output = $Trajectory <T>;
  fn neg (self)->$Trajectory <T> {
    $Trajectory {
      origin: self.origin,
      terms: Array::from_fn (| index | (& self.terms [index]).neg()),
    }
  }
}

  };
}

//impl_trajectory! (LinearTrajectory, 1) ;
impl_trajectory! (QuadraticTrajectory, 2) ;

<commit_msg>More work on trajectories<commit_after>//use nalgebra::Vector2;
//use std::cmp::max;
use num::traits::Signed;
use super::integer_math::*;
use std::ops::{Add, Sub, Mul, Neg, AddAssign, SubAssign, MulAssign};
use array_ext::*;

pub type Time = i64;

pub trait Trajectory {
  type Coefficient: Vector;
}

pub trait ScalarTrajectory: Trajectory {
  /*fn next_time_ge (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {

  }
  fn next_time_gt (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    self.next_time_ge (now, target + Vector::one())
  }
  fn next_time_le (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    (-self).next_time_ge (now, -target)
  }
  fn next_time_lt (&self, start: Time, end: Time, target: Self::Coefficient)->Option <Time> {
    self.next_time_le (now, target - Vector::one())
  }*/
}

macro_rules! impl_binop {
  ([$($generic_parameters: tt)*], $Left:ty, $Right:ty, $Trait: ident, $method: ident, $TraitAssign: ident, $method_assign: ident, $self: ident, $other: ident, $owned_owned: expr, $owned_ref: expr, $ref_owned: expr, $ref_ref: expr, $assign_owned: expr, $assign_ref: expr,) => {

impl <$($generic_parameters)*> $Trait <$Right> for $Left {
  type Output = Self;
  fn $method ($self, $other: $Right)->Self {
    $owned_owned
  }
}

impl <'a, $($generic_parameters)*> $Trait <& 'a $Right> for $Left {
  type Output = Self;
  fn $method ($self, $other: & 'a $Right)->Self {
    $owned_ref
  }
}

impl <'a, $($generic_parameters)*> $Trait <$Right> for & 'a $Left {
  type Output = $Left;
  fn $method ($self, $other: $Right)->$Left {
    $ref_owned
  }
}

impl <'a, 'b, $($generic_parameters)*> $Trait <& 'b $Right> for & 'a $Left {
  type Output = $Left;
  fn $method ($self, $other: & 'b $Right)->$Left {
    $ref_ref
  }
}

impl <$($generic_parameters)*> $TraitAssign <$Right> for $Left {
  fn $method_assign (&mut $self, $other: $Right) {
    $assign_owned
  }
}

impl <'a, $($generic_parameters)*> $TraitAssign <& 'a $Right> for $Left {
  fn $method_assign (&mut $self, $other: & 'a $Right) {
    $assign_ref
  }
}

  }
}

macro_rules! impl_trajectory_add_sub {
  ($Trajectory:ident, $Trait: ident, $method: ident, $TraitAssign: ident, $method_assign: ident) => {

impl_binop! {
[T: Vector], $Trajectory <T>, $Trajectory <T>, $Trait, $method, $TraitAssign, $method_assign, self, other,
{
  self += other;
  self
},
{
  self += other;
  self
},
{
  self.clone() + other
},
{
  self.clone() + other.clone()
},
{
  if self.origin < other.origin {self.set_origin (other.origin).unwrap();}
  if other.origin < self.origin {other.set_origin (self.origin).unwrap();}
  for (mine, others) in self.terms.iter_mut().zip (other.terms.into_iter()) {
    mine.$method_assign (others);
  }
},
{
  let mut other = other;
  let mut other_clone;
  if self.origin < other.origin {self.set_origin (other.origin).unwrap();}
  if other.origin < self.origin {
    other_clone = other.clone();
    other_clone.set_origin (self.origin).unwrap();
    other = & other_clone;
  }
  for (mine, others) in self.terms.iter_mut().zip (other.terms.iter()) {
    mine.$method_assign (others);
  }
},
}

impl_binop! {
[T: Vector], $Trajectory <T>, T, $Trait, $method, $TraitAssign, $method_assign, self, other,
{
  self.terms[0].$method_assign (other);
  self
},
{
  self.terms[0].$method_assign (other);
  self
},
{
  self.clone() + other
},
{
  self.clone() + other
},
{
  self.terms[0].$method_assign (other);
},
{
  self.terms[0].$method_assign (other);
},
}

  }
}

macro_rules! impl_trajectory {
  ($Trajectory: ident, $degree: expr) => {

#[derive (Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, Default)]
pub struct $Trajectory <T> {
  origin: Time,
  terms: [T; $degree + 1],
}

impl <T: Vector> $Trajectory <T> {
  pub fn constant (value: T)->Self {
    let terms = [T::zero(); $degree + 1];
    terms [0] = value;
    $Trajectory {origin: 0, terms}
  }
  pub fn set_origin (&mut self, new_origin: Time)->Result <(), polynomial::Error <Time>> {
    polynomial::translate (&mut self.terms, new_origin - self.origin)?;
    self.origin = new_origin;
    Ok (())
  }
  pub fn nth_term (&self, which: usize, time_numerator: Time, time_shift: u32)->Result <T, polynomial::OverflowError> {
    polynomial::evaluate_nth_taylor_coefficient_at_fractional_input (& self.terms, time - (self.origin << time_shift)), which)
  }
  pub fn set_nth_term (&mut self, which: usize, time_numerator: Time, time_shift: u32) {
    self.set_origin (time_numerator >> time_shift) ;
    unimplemented!()
  }
  pub fn add_nth_term (&mut self, which: usize, time_numerator: Time, time_shift: u32) {
    self.set_origin (time_numerator >> time_shift);
    unimplemented!()
  }
  pub fn value (&mut self, time_numerator: Time, time_shift: u32)->Result <T, polynomial::OverflowError> {self.term (0, time_numerator, time_shift)}
  pub fn velocity (&mut self, time_numerator: Time, time_shift: u32)->Result <T, polynomial::OverflowError> {self.term (1, time_numerator, time_shift)}
  pub fn set_value (&mut self, time_numerator: Time, time_shift: u32, value: T) {self.set_term (0, time_numerator, time_shift, value)}
  pub fn set_velocity (&mut self, time_numerator: Time, time_shift: u32, value: T) {self.set_term (1, time_numerator, time_shift, value)}
  pub fn add_value (&mut self, value: T) {self += value}
  pub fn add_velocity (&mut self, time_numerator: Time, time_shift: u32, value: T) {self.add_term (1, time_numerator, time_shift, value)}
}

impl <T: Integer + Signed> $Trajectory <T> {
  pub fn next_time_lt (&self, now: Time, value: T)->Option <Time> {
    if self.value (now) < value {Some (now)}
    else {
      unimplemented!()
    }
  }
  pub fn next_time_le (&self, now: Time, value: T)->Option <Time> {
    self.next_time_lt (now, value + T::one())
  }
  pub fn next_time_gt (&self, now: Time, value: T)->Option <Time> {
    (-self).next_time_lt (now, -value)
  }
  pub fn next_time_ge (&self, now: Time, value: T)->Option <Time> {
    self.next_time_gt (now, value - T::one())
  }
}

impl_trajectory_add_sub! ($Trajectory, Add, add, AddAssign, add_assign);
impl_trajectory_add_sub! ($Trajectory, Sub, sub, SubAssign, sub_assign);

impl<T: Vector, Coordinate> Mul<Coordinate> for $Trajectory<T> where T: MulAssign <Coordinate> {
  type Output = Self;
  fn mul(self, other: Coordinate) -> Self {
    self *= other; self
  }
}

impl<'a, T: Vector, Coordinate> Mul<Coordinate> for & 'a $Trajectory<T> where T: MulAssign <Coordinate> {
  type Output = $Trajectory <T>;
  fn mul(self, other: Coordinate) ->$Trajectory <T> {
    self.clone()*other
  }
}

impl<T: Vector, Coordinate> MulAssign <Coordinate> for $Trajectory<T> where T: MulAssign <Coordinate> {
  fn mul_assign (&mut self, other: Coordinate) {
    for term in self.terms.iter_mut (){MulAssign::<Coordinate>::mul_assign (term, other);}
  }
}

/*impl_binop! {
[T: Vector], $Trajectory <T>, T::Coordinate, Mul, mul, MulAssign, mul_assign, self, other,
{
  self *= other;
  self
},
{
  self *= other;
  self
},
{
  self.clone()*other
},
{
  self.clone()*other
},
{
  for term in self.terms.iter_mut (){*term *= other;}
},
{
  for term in self.terms.iter_mut (){*term *= other;}
},
}*/

impl <T: Vector + Neg <Output = T>> Neg for $Trajectory <T> {
  type Output = Self;
  fn neg (self)->Self {
    $Trajectory {
      origin: self.origin,
      terms: Array::from_fn (| index | self.terms [index].clone().neg()),
    }
  }
}
impl <'a, T: Vector> Neg for & 'a $Trajectory <T> where & 'a T: Neg <Output = T> {
  type Output = $Trajectory <T>;
  fn neg (self)->$Trajectory <T> {
    $Trajectory {
      origin: self.origin,
      terms: Array::from_fn (| index | (& self.terms [index]).neg()),
    }
  }
}

  };
}

//impl_trajectory! (LinearTrajectory, 1) ;
impl_trajectory! (QuadraticTrajectory, 2) ;

<|endoftext|>


<commit_before>//! Handles build script specific information

use std::{
    ffi::OsStr,
    io::BufReader,
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

use anyhow::Result;
use cargo_metadata::{BuildScript, Message, Package, PackageId};
use itertools::Itertools;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use stdx::JodChild;

use crate::{cfg_flag::CfgFlag, CargoConfig};

#[derive(Debug, Clone, Default)]
pub(crate) struct BuildDataMap {
    data: FxHashMap<PackageId, BuildData>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BuildData {
    /// List of config flags defined by this package's build script
    pub cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub envs: Vec<(String, String)>,
    /// Directory where a build script might place its output
    pub out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros
    pub proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl BuildDataMap {
    pub(crate) fn new(
        cargo_toml: &Path,
        cargo_features: &CargoConfig,
        packages: &Vec<Package>,
        progress: &dyn Fn(String),
    ) -> Result<BuildDataMap> {
        let mut cmd = Command::new(toolchain::cargo());
        cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
            .arg(cargo_toml);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &cargo_features.target {
            cmd.args(&["--target", target]);
        }

        if cargo_features.all_features {
            cmd.arg("--all-features");
        } else {
            if cargo_features.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                cmd.arg("--no-default-features");
            }
            if !cargo_features.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(cargo_features.features.join(" "));
            }
        }

        cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());

        let mut child = cmd.spawn().map(JodChild)?;
        let child_stdout = child.stdout.take().unwrap();
        let stdout = BufReader::new(child_stdout);

        let mut res = BuildDataMap::default();
        for message in cargo_metadata::Message::parse_stream(stdout) {
            if let Ok(message) = message {
                match message {
                    Message::BuildScriptExecuted(BuildScript {
                        package_id,
                        out_dir,
                        cfgs,
                        env,
                        ..
                    }) => {
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
                                    }
                                };
                            }
                            acc
                        };
                        let res = res.data.entry(package_id.clone()).or_default();
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if out_dir != PathBuf::default() {
                            let out_dir = AbsPathBuf::assert(out_dir);
                            res.out_dir = Some(out_dir);
                            res.cfgs = cfgs;
                        }

                        res.envs = env;
                    }
                    Message::CompilerArtifact(message) => {
                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.contains(&"proc-macro".to_string()) {
                            let package_id = message.package_id;
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(filename.clone());
                                let res = res.data.entry(package_id.clone()).or_default();
                                res.proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name.clone());
                    }
                    Message::Unknown => (),
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                }
            }
        }
        res.inject_cargo_env(packages);
        Ok(res)
    }

    pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self {
        let mut res = Self::default();
        res.inject_cargo_env(packages);
        res
    }

    pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> {
        self.data.get(id)
    }

    fn inject_cargo_env(&mut self, packages: &Vec<Package>) {
        for meta_pkg in packages {
            let resource = self.data.entry(meta_pkg.id.clone()).or_default();
            inject_cargo_env(meta_pkg, &mut resource.envs);

            if let Some(out_dir) = &resource.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
                    resource.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }
    }
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}

/// Recreates the compile-time environment variables that Cargo sets.
///
/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
    // FIXME: Missing variables:
    // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>

    let mut manifest_dir = package.manifest_path.clone();
    manifest_dir.pop();
    if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
        env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
    }

    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));

    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));

    let authors = package.authors.join(";");
    env.push(("CARGO_PKG_AUTHORS".into(), authors));

    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));

    let license_file =
        package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
}
<commit_msg>Use AbsPath<commit_after>//! Handles build script specific information

use std::{
    ffi::OsStr,
    io::BufReader,
    path::{Path, PathBuf},
    process::{Command, Stdio},
};

use anyhow::Result;
use cargo_metadata::{BuildScript, Message, Package, PackageId};
use itertools::Itertools;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use stdx::JodChild;

use crate::{cfg_flag::CfgFlag, CargoConfig};

#[derive(Debug, Clone, Default)]
pub(crate) struct BuildDataMap {
    data: FxHashMap<PackageId, BuildData>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BuildData {
    /// List of config flags defined by this package's build script
    pub cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub envs: Vec<(String, String)>,
    /// Directory where a build script might place its output
    pub out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros
    pub proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl BuildDataMap {
    pub(crate) fn new(
        cargo_toml: &AbsPath,
        cargo_features: &CargoConfig,
        packages: &Vec<Package>,
        progress: &dyn Fn(String),
    ) -> Result<BuildDataMap> {
        let mut cmd = Command::new(toolchain::cargo());
        cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
            .arg(cargo_toml.as_ref());

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &cargo_features.target {
            cmd.args(&["--target", target]);
        }

        if cargo_features.all_features {
            cmd.arg("--all-features");
        } else {
            if cargo_features.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                cmd.arg("--no-default-features");
            }
            if !cargo_features.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(cargo_features.features.join(" "));
            }
        }

        cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());

        let mut child = cmd.spawn().map(JodChild)?;
        let child_stdout = child.stdout.take().unwrap();
        let stdout = BufReader::new(child_stdout);

        let mut res = BuildDataMap::default();
        for message in cargo_metadata::Message::parse_stream(stdout) {
            if let Ok(message) = message {
                match message {
                    Message::BuildScriptExecuted(BuildScript {
                        package_id,
                        out_dir,
                        cfgs,
                        env,
                        ..
                    }) => {
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
                                    }
                                };
                            }
                            acc
                        };
                        let res = res.data.entry(package_id.clone()).or_default();
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if out_dir != PathBuf::default() {
                            let out_dir = AbsPathBuf::assert(out_dir);
                            res.out_dir = Some(out_dir);
                            res.cfgs = cfgs;
                        }

                        res.envs = env;
                    }
                    Message::CompilerArtifact(message) => {
                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.contains(&"proc-macro".to_string()) {
                            let package_id = message.package_id;
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(filename.clone());
                                let res = res.data.entry(package_id.clone()).or_default();
                                res.proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name.clone());
                    }
                    Message::Unknown => (),
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                }
            }
        }
        res.inject_cargo_env(packages);
        Ok(res)
    }

    pub(crate) fn with_cargo_env(packages: &Vec<Package>) -> Self {
        let mut res = Self::default();
        res.inject_cargo_env(packages);
        res
    }

    pub(crate) fn get(&self, id: &PackageId) -> Option<&BuildData> {
        self.data.get(id)
    }

    fn inject_cargo_env(&mut self, packages: &Vec<Package>) {
        for meta_pkg in packages {
            let resource = self.data.entry(meta_pkg.id.clone()).or_default();
            inject_cargo_env(meta_pkg, &mut resource.envs);

            if let Some(out_dir) = &resource.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
                    resource.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }
    }
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}

/// Recreates the compile-time environment variables that Cargo sets.
///
/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) {
    // FIXME: Missing variables:
    // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>

    let mut manifest_dir = package.manifest_path.clone();
    manifest_dir.pop();
    if let Some(cargo_manifest_dir) = manifest_dir.to_str() {
        env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into()));
    }

    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));

    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));

    let authors = package.authors.join(";");
    env.push(("CARGO_PKG_AUTHORS".into(), authors));

    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));

    let license_file =
        package.license_file.as_ref().map(|buf| buf.display().to_string()).unwrap_or_default();
    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
}
<|endoftext|>


<commit_before>#[allow(unused)]
macro_rules! features {
    (
      @TARGET: $target:ident;
      @CFG: $cfg:meta;
      @MACRO_NAME: $macro_name:ident;
      @MACRO_ATTRS: $(#[$macro_attrs:meta])*
      $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; )*
      $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
      $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt; $(#[$feature_comment:meta])*)*
    ) => {
        #[macro_export]
        $(#[$macro_attrs])*
        #[allow_internal_unstable(stdsimd_internal)]
        #[cfg($cfg)]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    cfg!(target_feature = $feature_lit) ||
                        $crate::detect::__is_feature_detected::$feature()
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        $(#[$macro_attrs])*
        #[macro_export]
        #[cfg(not($cfg))]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    compile_error!(
                        concat!(
                            r#"This macro cannot be used on the current target.
                            You can prevent it from being used in other architectures by
                            guarding it behind a cfg("#,
                            stringify!($cfg),
                            ")."
                        )
                    )
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        /// Each variant denotes a position in a bitset for a particular feature.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[derive(Copy, Clone)]
        #[repr(u8)]
        #[unstable(feature = "stdsimd_internal", issue = "none")]
        #[cfg($cfg)]
        pub(crate) enum Feature {
            $(
                $(#[$feature_comment])*
                $feature,
            )*

            // Do not add variants after last:
            _last
        }

        #[cfg($cfg)]
        impl Feature {
            pub(crate) fn to_str(self) -> &'static str {
                match self {
                    $(Feature::$feature => $feature_lit,)*
                    Feature::_last => unreachable!(),
                }
            }
            #[cfg(feature = "std_detect_env_override")]
            pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
                match s {
                    $($feature_lit => Ok(Feature::$feature),)*
                    _ => Err(())
                }
            }
        }

        /// Each function performs run-time feature detection for a single
        /// feature. This allow us to use stability attributes on a per feature
        /// basis.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[cfg($cfg)]
        pub mod __is_feature_detected {
            $(

                /// PLEASE: do not use this, it is an implementation detail
                /// subject to change.
                #[inline]
                #[doc(hidden)]
                #[$stability_attr]
                pub fn $feature() -> bool {
                    $crate::detect::check_for($crate::detect::Feature::$feature)
                }
            )*
        }
    };
}
<commit_msg>Add  stdsimd feature to allow_internal_unstable attribute in feature detect macros (#1303)<commit_after>#[allow(unused)]
macro_rules! features {
    (
      @TARGET: $target:ident;
      @CFG: $cfg:meta;
      @MACRO_NAME: $macro_name:ident;
      @MACRO_ATTRS: $(#[$macro_attrs:meta])*
      $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; )*
      $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
      $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt; $(#[$feature_comment:meta])*)*
    ) => {
        #[macro_export]
        $(#[$macro_attrs])*
        #[allow_internal_unstable(stdsimd_internal, stdsimd)]
        #[cfg($cfg)]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    cfg!(target_feature = $feature_lit) ||
                        $crate::detect::__is_feature_detected::$feature()
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        $(#[$macro_attrs])*
        #[macro_export]
        #[cfg(not($cfg))]
        #[doc(cfg($cfg))]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    compile_error!(
                        concat!(
                            r#"This macro cannot be used on the current target.
                            You can prevent it from being used in other architectures by
                            guarding it behind a cfg("#,
                            stringify!($cfg),
                            ")."
                        )
                    )
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!($nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        /// Each variant denotes a position in a bitset for a particular feature.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[derive(Copy, Clone)]
        #[repr(u8)]
        #[unstable(feature = "stdsimd_internal", issue = "none")]
        #[cfg($cfg)]
        pub(crate) enum Feature {
            $(
                $(#[$feature_comment])*
                $feature,
            )*

            // Do not add variants after last:
            _last
        }

        #[cfg($cfg)]
        impl Feature {
            pub(crate) fn to_str(self) -> &'static str {
                match self {
                    $(Feature::$feature => $feature_lit,)*
                    Feature::_last => unreachable!(),
                }
            }
            #[cfg(feature = "std_detect_env_override")]
            pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
                match s {
                    $($feature_lit => Ok(Feature::$feature),)*
                    _ => Err(())
                }
            }
        }

        /// Each function performs run-time feature detection for a single
        /// feature. This allow us to use stability attributes on a per feature
        /// basis.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[cfg($cfg)]
        pub mod __is_feature_detected {
            $(

                /// PLEASE: do not use this, it is an implementation detail
                /// subject to change.
                #[inline]
                #[doc(hidden)]
                #[$stability_attr]
                pub fn $feature() -> bool {
                    $crate::detect::check_for($crate::detect::Feature::$feature)
                }
            )*
        }
    };
}
<|endoftext|>