<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/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 serde_json::Value;
use std::default::Default;
use std::fmt::Display;
use ::internal::prelude::*;
use ::model::Embed;

#[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/struct.ChannelId.html#method.send_message
/// [`Embed`]: ../model/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub Map<String, 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 author = f(CreateEmbedAuthor::default()).0;

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

        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".to_owned(), Value::Number(Number::from(colour.into().0 as u64)));

        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".to_owned(), 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(mut self, description: &str) -> Self {
        self.0.insert("description".to_owned(), Value::String(description.to_owned()));

        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<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedField) -> CreateEmbedField {
        let field = f(CreateEmbedField::default()).0;

        {
            let key = "fields".to_owned();

            let entry = self.0.remove(&key).unwrap_or_else(|| Value::Array(vec![]));
            let mut arr = match entry {
                Value::Array(inner) => inner,
                _ => {
                    // The type of `entry` should always be a `Value::Array`.
                    //
                    // Theoretically this never happens, but you never know.
                    //
                    // In the event that it does, just return the current value.
                    return CreateEmbed(self.0);
                },
            };
            arr.push(Value::Object(field));

            self.0.insert("fields".to_owned(), Value::Array(arr));
        }

        CreateEmbed(self.0)
    }

    /// 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;

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

        CreateEmbed(self.0)
    }

    /// Set the image associated with the embed. This only supports HTTP(S).
    pub fn image(mut self, url: &str) -> Self {
        let image = json!({
            "url": url.to_owned()
        });

        self.0.insert("image".to_owned(), image);

        CreateEmbed(self.0)
    }

    /// Set the thumbnail of the embed. This only supports HTTP(S).
    pub fn thumbnail(mut self, url: &str) -> Self {
        let thumbnail = json!({
            "url": url.to_owned(),
        });

        self.0.insert("thumbnail".to_owned(), thumbnail);

        CreateEmbed(self.0)
    }

    /// 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::Client;
    /// #
    /// # let mut client = Client::new("");
    /// #
    /// client.on_message(|_, msg| {
    ///     if msg.content == "~embed" {
    ///         let _ = msg.channel_id.send_message(|m| m
    ///             .embed(|e| e
    ///                 .title("hello")
    ///                 .timestamp("2004-06-08T16:04:23")));
    ///     }
    /// });
    /// ```
    ///
    /// Creating a join-log:
    ///
    /// Note: this example isn't efficient and is for demonstrative purposes.
    ///
    /// ```rust,no_run
    /// # use serenity::Client;
    /// #
    /// # let mut client = Client::new("");
    /// #
    /// use serenity::CACHE;
    ///
    /// client.on_guild_member_add(|_, guild_id, member| {
    ///     let cache = CACHE.read().unwrap();
    ///
    ///     if let Some(guild) = cache.guild(guild_id) {
    ///         let guild = guild.read().unwrap();
    ///
    ///         let channel_search = guild
    ///             .channels
    ///             .values()
    ///             .find(|c| c.read().unwrap().name == "join-log");
    ///
    ///         if let Some(channel) = channel_search {
    ///             let user = member.user.read().unwrap();
    ///
    ///             let _ = channel.read().unwrap().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
    ///                 }));
    ///         }
    ///     }
    /// });
    /// ```
    pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
        self.0.insert("timestamp".to_owned(), Value::String(timestamp.into().ts));

        CreateEmbed(self.0)
    }

    /// Set the title of the embed.
    pub fn title(mut self, title: &str) -> Self {
        self.0.insert("title".to_owned(), Value::String(title.to_owned()));

        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".to_owned(), Value::String(url.to_owned()));

        CreateEmbed(self.0)
    }
}

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

        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(move |f| f
                .inline(field.inline)
                .name(&field.name)
                .value(&field.value));
        }

        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);
        }

        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/struct.Embed.html
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub Map<String, 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".to_owned(), Value::String(icon_url.to_owned()));

        self
    }

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

        self
    }

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

        self
    }
}

/// A builder to create a fake [`Embed`] object's field, for use with the
/// [`CreateEmbed::field`] method.
///
/// This does not require any field be set. `inline` is set to `true` by
/// default.
///
/// [`Embed`]: ../model/struct.Embed.html
/// [`CreateEmbed::field`]: struct.CreateEmbed.html#method.field
#[derive(Clone, Debug)]
pub struct CreateEmbedField(pub Map<String, Value>);

impl CreateEmbedField {
    /// Set whether the field is inlined. Set to true by default.
    pub fn inline(mut self, inline: bool) -> Self {
        self.0.insert("inline".to_owned(), Value::Bool(inline));

        self
    }

    /// Set the field's name. It can't be longer than 256 characters.
    pub fn name(mut self, name: &str) -> Self {
        self.0.insert("name".to_owned(), Value::String(name.to_owned()));

        self
    }

    /// Set the field's value. It can't be longer than 1024 characters.
    pub fn value(mut self, value: &str) -> Self {
        self.0.insert("value".to_owned(), Value::String(value.to_owned()));

        self
    }
}

impl Default for CreateEmbedField {
    /// Creates a builder with default values, setting the value of `inline` to
    /// `true`.
    fn default() -> CreateEmbedField {
        let mut map = Map::new();
        map.insert("inline".to_owned(), Value::Bool(true));

        CreateEmbedField(map)
    }
}

/// 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/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub Map<String, 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".to_owned(), Value::String(icon_url.to_owned()));

        self
    }

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

        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_owned(),
        }
    }
}

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>Add support for sending attachments in embeds (#95)<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/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 serde_json::Value;
use std::default::Default;
use std::fmt::Display;
use ::internal::prelude::*;
use ::model::Embed;

#[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/struct.ChannelId.html#method.send_message
/// [`Embed`]: ../model/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub Map<String, 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 author = f(CreateEmbedAuthor::default()).0;

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

        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".to_owned(), Value::Number(Number::from(colour.into().0 as u64)));

        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".to_owned(), 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(mut self, description: &str) -> Self {
        self.0.insert("description".to_owned(), Value::String(description.to_owned()));

        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<F>(mut self, f: F) -> Self
        where F: FnOnce(CreateEmbedField) -> CreateEmbedField {
        let field = f(CreateEmbedField::default()).0;

        {
            let key = "fields".to_owned();

            let entry = self.0.remove(&key).unwrap_or_else(|| Value::Array(vec![]));
            let mut arr = match entry {
                Value::Array(inner) => inner,
                _ => {
                    // The type of `entry` should always be a `Value::Array`.
                    //
                    // Theoretically this never happens, but you never know.
                    //
                    // In the event that it does, just return the current value.
                    return CreateEmbed(self.0);
                },
            };
            arr.push(Value::Object(field));

            self.0.insert("fields".to_owned(), Value::Array(arr));
        }

        CreateEmbed(self.0)
    }

    /// 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;

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

        CreateEmbed(self.0)
    }

    /// Set the image associated with the embed. This only supports HTTP(S).
    pub fn image(mut self, url: &str) -> Self {
        let image = json!({
            "url": url.to_owned()
        });

        self.0.insert("image".to_owned(), image);

        CreateEmbed(self.0)
    }

    /// Set the thumbnail of the embed. This only supports HTTP(S).
    pub fn thumbnail(mut self, url: &str) -> Self {
        let thumbnail = json!({
            "url": url.to_owned(),
        });

        self.0.insert("thumbnail".to_owned(), thumbnail);

        CreateEmbed(self.0)
    }

    /// 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::Client;
    /// #
    /// # let mut client = Client::new("");
    /// #
    /// client.on_message(|_, msg| {
    ///     if msg.content == "~embed" {
    ///         let _ = msg.channel_id.send_message(|m| m
    ///             .embed(|e| e
    ///                 .title("hello")
    ///                 .timestamp("2004-06-08T16:04:23")));
    ///     }
    /// });
    /// ```
    ///
    /// Creating a join-log:
    ///
    /// Note: this example isn't efficient and is for demonstrative purposes.
    ///
    /// ```rust,no_run
    /// # use serenity::Client;
    /// #
    /// # let mut client = Client::new("");
    /// #
    /// use serenity::CACHE;
    ///
    /// client.on_guild_member_add(|_, guild_id, member| {
    ///     let cache = CACHE.read().unwrap();
    ///
    ///     if let Some(guild) = cache.guild(guild_id) {
    ///         let guild = guild.read().unwrap();
    ///
    ///         let channel_search = guild
    ///             .channels
    ///             .values()
    ///             .find(|c| c.read().unwrap().name == "join-log");
    ///
    ///         if let Some(channel) = channel_search {
    ///             let user = member.user.read().unwrap();
    ///
    ///             let _ = channel.read().unwrap().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
    ///                 }));
    ///         }
    ///     }
    /// });
    /// ```
    pub fn timestamp<T: Into<Timestamp>>(mut self, timestamp: T) -> Self {
        self.0.insert("timestamp".to_owned(), Value::String(timestamp.into().ts));

        CreateEmbed(self.0)
    }

    /// Set the title of the embed.
    pub fn title(mut self, title: &str) -> Self {
        self.0.insert("title".to_owned(), Value::String(title.to_owned()));

        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".to_owned(), Value::String(url.to_owned()));

        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/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 = Map::new();
        map.insert("type".to_owned(), Value::String("rich".to_owned()));

        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(move |f| f
                .inline(field.inline)
                .name(&field.name)
                .value(&field.value));
        }

        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);
        }

        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/struct.Embed.html
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub Map<String, 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".to_owned(), Value::String(icon_url.to_owned()));

        self
    }

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

        self
    }

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

        self
    }
}

/// A builder to create a fake [`Embed`] object's field, for use with the
/// [`CreateEmbed::field`] method.
///
/// This does not require any field be set. `inline` is set to `true` by
/// default.
///
/// [`Embed`]: ../model/struct.Embed.html
/// [`CreateEmbed::field`]: struct.CreateEmbed.html#method.field
#[derive(Clone, Debug)]
pub struct CreateEmbedField(pub Map<String, Value>);

impl CreateEmbedField {
    /// Set whether the field is inlined. Set to true by default.
    pub fn inline(mut self, inline: bool) -> Self {
        self.0.insert("inline".to_owned(), Value::Bool(inline));

        self
    }

    /// Set the field's name. It can't be longer than 256 characters.
    pub fn name(mut self, name: &str) -> Self {
        self.0.insert("name".to_owned(), Value::String(name.to_owned()));

        self
    }

    /// Set the field's value. It can't be longer than 1024 characters.
    pub fn value(mut self, value: &str) -> Self {
        self.0.insert("value".to_owned(), Value::String(value.to_owned()));

        self
    }
}

impl Default for CreateEmbedField {
    /// Creates a builder with default values, setting the value of `inline` to
    /// `true`.
    fn default() -> CreateEmbedField {
        let mut map = Map::new();
        map.insert("inline".to_owned(), Value::Bool(true));

        CreateEmbedField(map)
    }
}

/// 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/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub Map<String, 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".to_owned(), Value::String(icon_url.to_owned()));

        self
    }

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

        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_owned(),
        }
    }
}

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 cookie_factory;
extern crate test;
#[macro_use]
extern crate maplit;

use std::collections::BTreeMap;
use std::io::Write;
use std::str;
use test::Bencher;

use cookie_factory::*;

#[derive(Clone, Debug, PartialEq)]
pub enum JsonValue {
    Str(String),
    Boolean(bool),
    Num(f64),
    Array(Vec<JsonValue>),
    Object(BTreeMap<String, JsonValue>),
}

#[inline(always)]
pub fn gen_str<'a, 'b: 'a, W: Write>(s: &'b str) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("\"")(out)?;
        let out = string(s)(out)?;
        string("\"")(out)
    }
}

#[inline(always)]
pub fn gen_bool<'a, W: Write>(b: bool) -> impl SerializeFn<W> {
    if b {
        string("true")
    } else {
        string("false")
    }
}

#[inline(always)]
pub fn gen_num<'a, W: Write>(_b: f64) -> impl SerializeFn<W> {
    /*move |out: &'a mut [u8]| {
      let s = format!("{}", b);
      string(s)(out)
    }*/
    string("1234.56")
}

pub fn gen_array<'a, 'b: 'a, W: Write>(arr: &'b [JsonValue]) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("[")(out)?;
        let out = separated_list(string(","), arr.iter().map(gen_json_value))(out)?;
        string("]")(out)
    }
}

pub fn gen_key_value<'a, 'b: 'a, W: Write>(
    kv: (&'b String, &'b JsonValue),
) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = gen_str(kv.0)(out)?;
        let out = string(":")(out)?;
        gen_json_value(&kv.1)(out)
    }
}

pub fn gen_object<'a, 'b: 'a, W: Write>(
    o: &'b BTreeMap<String, JsonValue>,
) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("{")(out)?;

        let out = separated_list(string(","), o.iter().map(gen_key_value))(out)?;
        string("}")(out)
    }
}

pub fn gen_json_value<'a, W: Write>(g: &'a JsonValue) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| match g {
        JsonValue::Str(ref s) => gen_str(s)(out),
        JsonValue::Boolean(ref b) => gen_bool(*b)(out),
        JsonValue::Num(ref n) => gen_num(*n)(out),
        JsonValue::Array(ref v) => gen_array(v)(out),
        JsonValue::Object(ref o) => gen_object(o)(out),
    }
}

use std::iter::repeat;

#[test]
fn json_test() {
    let value = JsonValue::Object(btreemap! {
      String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]),
      String::from("b") => JsonValue::Boolean(true),
      String::from("o") => JsonValue::Object(btreemap!{
        String::from("x") => JsonValue::Str(String::from("abcd")),
        String::from("y") => JsonValue::Str(String::from("efgh")),
        String::from("empty") => JsonValue::Array(vec![]),
      }),
    });

    let mut buffer = repeat(0).take(16384).collect::<Vec<u8>>();
    let index = {
        let sr = gen_json_value(&value);

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    println!("result:\n{}", str::from_utf8(&buffer[..index]).unwrap());
    assert_eq!(str::from_utf8(&buffer[..index]).unwrap(),
    "{\"arr\":[1234.56,1234.56,1234.56],\"b\":true,\"o\":{\"empty\":[],\"x\":\"abcd\",\"y\":\"efgh\"}}");
    panic!();
}

#[bench]
fn functions_json(b: &mut Bencher) {
    let element = JsonValue::Object(btreemap! {
      String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]),
      String::from("b") => JsonValue::Boolean(true),
      String::from("o") => JsonValue::Object(btreemap!{
        String::from("x") => JsonValue::Str(String::from("abcd")),
        String::from("y") => JsonValue::Str(String::from("efgh")),
        String::from("empty") => JsonValue::Array(vec![]),
      }),
    });

    let value = JsonValue::Array(repeat(element).take(10).collect::<Vec<JsonValue>>());

    let mut buffer = repeat(0u8).take(16384).collect::<Vec<_>>();
    let index = {
        let sr = gen_json_value(&value);

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    b.bytes = index as u64;
    b.iter(|| {
        let sr = gen_json_value(&value);
        let _ = gen_simple(sr, &mut buffer[..]).unwrap();
    });
}

#[bench]
fn functions_gen_str(b: &mut Bencher) {
    let mut buffer = repeat(0).take(16384).collect::<Vec<u8>>();

    let index = {
        let sr = gen_str(&"hello");

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    b.bytes = index as u64;
    b.iter(|| {
        let sr = gen_str(&"hello");
        let _ = gen_simple(sr, &mut buffer[..]).unwrap();
    });
}
<commit_msg>fix benchmark build<commit_after>#![feature(test)]
extern crate cookie_factory;
extern crate test;
#[macro_use]
extern crate maplit;

use std::collections::BTreeMap;
use std::io::Write;
use std::str;
use test::Bencher;

use cookie_factory::*;
use cookie_factory::{
    combinator::string,
    multi::separated_list,
};

#[derive(Clone, Debug, PartialEq)]
pub enum JsonValue {
    Str(String),
    Boolean(bool),
    Num(f64),
    Array(Vec<JsonValue>),
    Object(BTreeMap<String, JsonValue>),
}

#[inline(always)]
pub fn gen_str<'a, 'b: 'a, W: Write>(s: &'b str) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("\"")(out)?;
        let out = string(s)(out)?;
        string("\"")(out)
    }
}

#[inline(always)]
pub fn gen_bool<'a, W: Write>(b: bool) -> impl SerializeFn<W> {
    if b {
        string("true")
    } else {
        string("false")
    }
}

#[inline(always)]
pub fn gen_num<'a, W: Write>(_b: f64) -> impl SerializeFn<W> {
    /*move |out: &'a mut [u8]| {
      let s = format!("{}", b);
      string(s)(out)
    }*/
    string("1234.56")
}

pub fn gen_array<'a, 'b: 'a, W: Write>(arr: &'b [JsonValue]) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("[")(out)?;
        let out = separated_list(string(","), arr.iter().map(gen_json_value))(out)?;
        string("]")(out)
    }
}

pub fn gen_key_value<'a, 'b: 'a, W: Write>(
    kv: (&'b String, &'b JsonValue),
) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = gen_str(kv.0)(out)?;
        let out = string(":")(out)?;
        gen_json_value(&kv.1)(out)
    }
}

pub fn gen_object<'a, 'b: 'a, W: Write>(
    o: &'b BTreeMap<String, JsonValue>,
) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| {
        let out = string("{")(out)?;

        let out = separated_list(string(","), o.iter().map(gen_key_value))(out)?;
        string("}")(out)
    }
}

pub fn gen_json_value<'a, W: Write>(g: &'a JsonValue) -> impl SerializeFn<W> + 'a {
    move |out: WriteContext<W>| match g {
        JsonValue::Str(ref s) => gen_str(s)(out),
        JsonValue::Boolean(ref b) => gen_bool(*b)(out),
        JsonValue::Num(ref n) => gen_num(*n)(out),
        JsonValue::Array(ref v) => gen_array(v)(out),
        JsonValue::Object(ref o) => gen_object(o)(out),
    }
}

use std::iter::repeat;

#[test]
fn json_test() {
    let value = JsonValue::Object(btreemap! {
      String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]),
      String::from("b") => JsonValue::Boolean(true),
      String::from("o") => JsonValue::Object(btreemap!{
        String::from("x") => JsonValue::Str(String::from("abcd")),
        String::from("y") => JsonValue::Str(String::from("efgh")),
        String::from("empty") => JsonValue::Array(vec![]),
      }),
    });

    let mut buffer = repeat(0).take(16384).collect::<Vec<u8>>();
    let index = {
        let sr = gen_json_value(&value);

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    println!("result:\n{}", str::from_utf8(&buffer[..index]).unwrap());
    assert_eq!(str::from_utf8(&buffer[..index]).unwrap(),
    "{\"arr\":[1234.56,1234.56,1234.56],\"b\":true,\"o\":{\"empty\":[],\"x\":\"abcd\",\"y\":\"efgh\"}}");
    panic!();
}

#[bench]
fn functions_json(b: &mut Bencher) {
    let element = JsonValue::Object(btreemap! {
      String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]),
      String::from("b") => JsonValue::Boolean(true),
      String::from("o") => JsonValue::Object(btreemap!{
        String::from("x") => JsonValue::Str(String::from("abcd")),
        String::from("y") => JsonValue::Str(String::from("efgh")),
        String::from("empty") => JsonValue::Array(vec![]),
      }),
    });

    let value = JsonValue::Array(repeat(element).take(10).collect::<Vec<JsonValue>>());

    let mut buffer = repeat(0u8).take(16384).collect::<Vec<_>>();
    let index = {
        let sr = gen_json_value(&value);

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    b.bytes = index as u64;
    b.iter(|| {
        let sr = gen_json_value(&value);
        let _ = gen_simple(sr, &mut buffer[..]).unwrap();
    });
}

#[bench]
fn functions_gen_str(b: &mut Bencher) {
    let mut buffer = repeat(0).take(16384).collect::<Vec<u8>>();

    let index = {
        let sr = gen_str(&"hello");

        let (_, res) = gen(sr, &mut buffer[..]).unwrap();
        res as usize
    };

    b.bytes = index as u64;
    b.iter(|| {
        let sr = gen_str(&"hello");
        let _ = gen_simple(sr, &mut buffer[..]).unwrap();
    });
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/System_time

extern crate time;
use time::{at, get_time, strftime};

fn main () {
    // Prints the current time as a timespec containing the seconds
    // and nanoseconds since 1970-01-01T00:00:00Z.
    let time_ts = get_time();
    println!("seconds: {} nanoseconds: {}", time_ts.sec, time_ts.nsec);

    // Comvert the timespec to a broken-down time value Tm
    // Could also use "let time_tm = now();" to get directly
    let time_tm = at(time_ts);

    // Display time formatted according to the asctime format in ISO
    // C, in the local timezone, eg "Wed Oct 29 22:26:17 2014"
    println!("ctime: {}", time_tm.ctime());

    // Display time formatted according to RFC 822,
    // eg "Wed, 29 Oct 2014 22:26:17"
    println!("rfc822: {}", time_tm.rfc822());

    // Display time formatted according to RFC 3339/ISO8601
    // eg "2014-10-29T22:26:17+07:00"
    println!("rfc3339: {}", time_tm.rfc3339());

    // Display time in a custom format (eg "22:26:17") using strftime
    println!("Custom: {}", strftime("%H:%M:%S", &time_tm).unwrap());
}
<commit_msg>Fixed typo in comments for system_time<commit_after>// http://rosettacode.org/wiki/System_time

extern crate time;
use time::{at, get_time, strftime};

fn main () {
    // Prints the current time as a timespec containing the seconds
    // and nanoseconds since 1970-01-01T00:00:00Z.
    let time_ts = get_time();
    println!("seconds: {} nanoseconds: {}", time_ts.sec, time_ts.nsec);

    // Convert the timespec to a broken-down time value Tm
    // Could also use "let time_tm = now();" to get directly
    let time_tm = at(time_ts);

    // Display time formatted according to the asctime format in ISO
    // C, in the local timezone, eg "Wed Oct 29 22:26:17 2014"
    println!("ctime: {}", time_tm.ctime());

    // Display time formatted according to RFC 822,
    // eg "Wed, 29 Oct 2014 22:26:17"
    println!("rfc822: {}", time_tm.rfc822());

    // Display time formatted according to RFC 3339/ISO8601
    // eg "2014-10-29T22:26:17+07:00"
    println!("rfc3339: {}", time_tm.rfc3339());

    // Display time in a custom format (eg "22:26:17") using strftime
    println!("Custom: {}", strftime("%H:%M:%S", &time_tm).unwrap());
}
<|endoftext|>


<commit_before>
use Error;
use Result;
use byteorder::{BigEndian, ByteOrder};

#[derive(Debug)]
pub struct CMAP {
    encoding_subtable: EncodingSubtable,
    cmap_offset: usize,
}

impl CMAP {
    pub fn from_data(data: &[u8], offset: usize) -> Result<Self> {

        if offset >= data.len() || offset + 4 > data.len() {
            return Err(Error::Malformed);
        }

        // +2 skip version field.
        let number_subtables = BigEndian::read_u16(&data[offset + 2..]) as usize;
        let data = &data[offset + 4..];
        if number_subtables * (2 + 2 + 4) > data.len() {
            return Err(Error::Malformed);
        }

        let mut encoding_subtables: Vec<_> = (0..number_subtables).filter_map(|n| {
            let z = n as usize * 8;
            let platform_id = BigEndian::read_u16(&data[z + 0..]);
            let platform_specific_id = BigEndian::read_u16(&data[z + 2..]);
            let offset = BigEndian::read_u32(&data[z + 4..]);
            Platform::new(platform_id, platform_specific_id).map(|platform| {
                EncodingSubtable { platform: platform, offset: offset}
            })
        }).collect();

        encoding_subtables.sort_by(|a, b| a.order().cmp(&b.order()));

        if encoding_subtables.is_empty() {
            return Err(Error::CMAPEncodingSubtableIsNotSupported);
        }

        Ok(CMAP {
            encoding_subtable: encoding_subtables.first().unwrap().clone(),
            cmap_offset: offset,
        })
    }

    pub fn index_map(&self) -> usize {
        self.encoding_subtable.offset as usize + self.cmap_offset
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct EncodingSubtable {
    platform: Platform,
    offset: u32,
}

impl EncodingSubtable {
    /// Defines an order in which the encoding subtables should be selected.
    fn order(&self) -> u32 {
        use self::Platform::*;
        use self::UnicodeEncodingId::*;
        use self::MicrosoftEncodingId::*;

        match self.platform {
            Unicode(Unicode20) => 0,
            Unicode(Unicode20BMPOnly) => 1,
            Unicode(Version11Semantics) => 1,
            Unicode(DefaultSemantics) => 1,
            Microsoft(UnicodeUCS4) => 2,
            Microsoft(UnicodeUCS2) => 3,
            Microsoft(Symbol) => 4,
            _ => 10,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Platform {
    Unicode(UnicodeEncodingId),
    Microsoft(MicrosoftEncodingId),
}

impl Platform {
    fn new(platform_id: u16, platform_specific_id: u16) -> Option<Self> {
        use self::Platform::*;
        use self::UnicodeEncodingId::*;
        use self::MicrosoftEncodingId::*;

        match platform_id {
            0 => match platform_specific_id {
                0 => Some(Unicode(DefaultSemantics)),
                1 => Some(Unicode(Version11Semantics)),
                3 => Some(Unicode(Unicode20BMPOnly)),
                4 => Some(Unicode(Unicode20)),
                5 => Some(Unicode(UnicodeVariationSequences)),
                6 => Some(Unicode(FullUnicodeCoverage)),
                _ => None,
            },
            3 => match platform_specific_id {
                0 => Some(Microsoft(Symbol)),
                1 => Some(Microsoft(UnicodeUCS2)),
                2 => Some(Microsoft(ShiftJIS)),
                3 => Some(Microsoft(PRC)),
                4 => Some(Microsoft(BigFive)),
                5 => Some(Microsoft(Johab)),
                10 => Some(Microsoft(UnicodeUCS4)),
                _ => None,
            },
            _ => None,
        }
    }
}

#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum UnicodeEncodingId {
    DefaultSemantics = 0,
    Version11Semantics = 1,
    Unicode20BMPOnly = 3,
    Unicode20 = 4,
    UnicodeVariationSequences = 5,
    FullUnicodeCoverage = 6,
}

#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum MicrosoftEncodingId {
    Symbol = 0,
    UnicodeUCS2 = 1,
    ShiftJIS = 2,
    PRC = 3,
    BigFive = 4,
    Johab = 5,
    UnicodeUCS4 = 10
}

#[derive(Debug)]
struct Format0 {
    format: u16,
    length: u16,
    language: u16,
    glyph_index_array: Vec<u8>,
}

impl Format0 {
    fn from_data(data: &[u8], offset: usize) -> Result<Self> {
        const SIZE: usize = 262;
        if offset + SIZE > data.len() {
            return Err(Error::Malformed);
        }

        let format = BigEndian::read_u16(&data[offset..]);
        let length = BigEndian::read_u16(&data[offset + 2..]);
        if length as usize != SIZE {
            return Err(Error::Malformed);
        }
        let language = BigEndian::read_u16(&data[offset + 4..]);

        Ok(Format0 {
            format: format,
            length: length,
            language: language,
            glyph_index_array: data[offset + 6..SIZE].to_owned(),
        })
    }

    fn index_for_code(&self, code: usize) -> Option<usize> {
        self.glyph_index_array.get(code).map(|&i| i as usize)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Error::*;
    use expectest::prelude::*;

    #[test]
    fn smoke() {
        let data = ::utils::read_file("tests/Tuffy_Bold.ttf");
        let offset = ::utils::find_table_offset(&data, 0, b"cmap").unwrap().unwrap();

        let _ = CMAP::from_data(&data, offset).unwrap();
    }
}
<commit_msg>Implement cmap's format 6.<commit_after>
use Error;
use Result;
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};

#[derive(Debug)]
pub struct CMAP {
    encoding_subtable: EncodingSubtable,
    cmap_offset: usize,
}

impl CMAP {
    pub fn from_data(data: &[u8], offset: usize) -> Result<Self> {

        if offset >= data.len() || offset + 4 > data.len() {
            return Err(Error::Malformed);
        }

        // +2 skip version field.
        let number_subtables = BigEndian::read_u16(&data[offset + 2..]) as usize;
        let data = &data[offset + 4..];
        if number_subtables * (2 + 2 + 4) > data.len() {
            return Err(Error::Malformed);
        }

        let mut encoding_subtables: Vec<_> = (0..number_subtables).filter_map(|n| {
            let z = n as usize * 8;
            let platform_id = BigEndian::read_u16(&data[z + 0..]);
            let platform_specific_id = BigEndian::read_u16(&data[z + 2..]);
            let offset = BigEndian::read_u32(&data[z + 4..]);
            Platform::new(platform_id, platform_specific_id).map(|platform| {
                EncodingSubtable { platform: platform, offset: offset}
            })
        }).collect();

        encoding_subtables.sort_by(|a, b| a.order().cmp(&b.order()));

        if encoding_subtables.is_empty() {
            return Err(Error::CMAPEncodingSubtableIsNotSupported);
        }

        Ok(CMAP {
            encoding_subtable: encoding_subtables.first().unwrap().clone(),
            cmap_offset: offset,
        })
    }

    pub fn index_map(&self) -> usize {
        self.encoding_subtable.offset as usize + self.cmap_offset
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct EncodingSubtable {
    platform: Platform,
    offset: u32,
}

impl EncodingSubtable {
    /// Defines an order in which the encoding subtables should be selected.
    fn order(&self) -> u32 {
        use self::Platform::*;
        use self::UnicodeEncodingId::*;
        use self::MicrosoftEncodingId::*;

        match self.platform {
            Unicode(Unicode20) => 0,
            Unicode(Unicode20BMPOnly) => 1,
            Unicode(Version11Semantics) => 1,
            Unicode(DefaultSemantics) => 1,
            Microsoft(UnicodeUCS4) => 2,
            Microsoft(UnicodeUCS2) => 3,
            Microsoft(Symbol) => 4,
            _ => 10,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Platform {
    Unicode(UnicodeEncodingId),
    Microsoft(MicrosoftEncodingId),
}

impl Platform {
    fn new(platform_id: u16, platform_specific_id: u16) -> Option<Self> {
        use self::Platform::*;
        use self::UnicodeEncodingId::*;
        use self::MicrosoftEncodingId::*;

        match platform_id {
            0 => match platform_specific_id {
                0 => Some(Unicode(DefaultSemantics)),
                1 => Some(Unicode(Version11Semantics)),
                3 => Some(Unicode(Unicode20BMPOnly)),
                4 => Some(Unicode(Unicode20)),
                5 => Some(Unicode(UnicodeVariationSequences)),
                6 => Some(Unicode(FullUnicodeCoverage)),
                _ => None,
            },
            3 => match platform_specific_id {
                0 => Some(Microsoft(Symbol)),
                1 => Some(Microsoft(UnicodeUCS2)),
                2 => Some(Microsoft(ShiftJIS)),
                3 => Some(Microsoft(PRC)),
                4 => Some(Microsoft(BigFive)),
                5 => Some(Microsoft(Johab)),
                10 => Some(Microsoft(UnicodeUCS4)),
                _ => None,
            },
            _ => None,
        }
    }
}

#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum UnicodeEncodingId {
    DefaultSemantics = 0,
    Version11Semantics = 1,
    Unicode20BMPOnly = 3,
    Unicode20 = 4,
    UnicodeVariationSequences = 5,
    FullUnicodeCoverage = 6,
}

#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum MicrosoftEncodingId {
    Symbol = 0,
    UnicodeUCS2 = 1,
    ShiftJIS = 2,
    PRC = 3,
    BigFive = 4,
    Johab = 5,
    UnicodeUCS4 = 10
}

#[derive(Debug)]
struct Format0 {
    format: u16,
    length: u16,
    language: u16,
    glyph_index_array: Vec<u8>,
}

impl Format0 {
    fn from_data(data: &[u8], offset: usize) -> Result<Self> {
        const SIZE: usize = 262;
        if offset + SIZE > data.len() {
            return Err(Error::Malformed);
        }

        let format = BigEndian::read_u16(&data[offset..]);
        let length = BigEndian::read_u16(&data[offset + 2..]);
        if length as usize != SIZE {
            return Err(Error::Malformed);
        }
        let language = BigEndian::read_u16(&data[offset + 4..]);

        Ok(Format0 {
            format: format,
            length: length,
            language: language,
            glyph_index_array: data[offset + 6..SIZE].to_owned(),
        })
    }

    fn index_for_code(&self, code: usize) -> Option<usize> {
        self.glyph_index_array.get(code).map(|&i| i as usize)
    }
}

#[derive(Debug)]
struct Format6 {
    format: u16,
    length: u16,
    language: u16,
    first_code: u16,
    entry_count: u16,
    raw_glyph_index_array: Vec<u8>,
}

impl Format6 {
    fn from_data(data: &[u8], offset: usize) -> Result<Self> {
        if offset + 2 * 5 > data.len() {
            return Err(Error::Malformed);
        }

        let format = BigEndian::read_u16(&data[offset..]);
        let length = BigEndian::read_u16(&data[offset + 2..]);
        let language = BigEndian::read_u16(&data[offset + 4..]);
        let first_code = BigEndian::read_u16(&data[offset + 6..]);
        let entry_count = BigEndian::read_u16(&data[offset + 8..]);

        let size = entry_count as usize * 2;
        if offset + 2 * 5 + size > data.len() {
            return Err(Error::Malformed);
        }

        Ok(Format6 {
            format: format,
            length: length,
            language: language,
            first_code: first_code,
            entry_count: entry_count,
            raw_glyph_index_array: data[offset + 2 * 5..size].to_owned(),
        })
    }

    fn index_for_code(&self, code: usize) -> Option<usize> {
        let first_code = self.first_code as usize;
        let entry_count = self.entry_count as usize;
        if code < first_code || code >= first_code + entry_count {
            None
        } else {
            let offset = (code - first_code) * 2;
            if offset >= self.raw_glyph_index_array.len() {
                None
            } else {
                Some(BigEndian::read_u16(&self.raw_glyph_index_array[offset..]) as usize)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use Error::*;
    use expectest::prelude::*;

    #[test]
    fn smoke() {
        let data = ::utils::read_file("tests/Tuffy_Bold.ttf");
        let offset = ::utils::find_table_offset(&data, 0, b"cmap").unwrap().unwrap();

        let _ = CMAP::from_data(&data, offset).unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::{
    user::UserShowPrivateResponse,
    util::{header, StatusCode},
    RequestHelper, TestApp,
};
use cargo_registry::{
    models::ApiToken,
    schema::api_tokens,
    util::errors::TOKEN_FORMAT_ERROR,
    views::{EncodableApiTokenWithToken, EncodableMe},
};
use std::collections::HashSet;

use diesel::prelude::*;

#[derive(Deserialize)]
struct DecodableApiToken {
    name: String,
}

#[derive(Deserialize)]
struct ListResponse {
    api_tokens: Vec<DecodableApiToken>,
}
#[derive(Deserialize)]
struct NewResponse {
    api_token: EncodableApiTokenWithToken,
}
#[derive(Deserialize)]
struct RevokedResponse {}

macro_rules! assert_contains {
    ($e:expr, $f:expr) => {
        if !$e.contains($f) {
            panic!(format!("expected '{}' to contain '{}'", $e, $f));
        }
    };
}

// Default values used by many tests
static URL: &str = "/api/v1/me/tokens";
static NEW_BAR: &[u8] = br#"{ "api_token": { "name": "bar" } }"#;

#[test]
fn list_logged_out() {
    let (_, anon) = TestApp::init().empty();
    anon.get(URL).assert_forbidden();
}

#[test]
fn list_empty() {
    let (_, _, user) = TestApp::init().with_user();
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), 0);
}

#[test]
fn list_tokens() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    let tokens = app.db(|conn| {
        vec![
            assert_ok!(ApiToken::insert(conn, id, "bar")),
            assert_ok!(ApiToken::insert(conn, id, "baz")),
        ]
    });

    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len());
    assert_eq!(
        json.api_tokens
            .into_iter()
            .map(|t| t.name)
            .collect::<HashSet<_>>(),
        tokens
            .into_iter()
            .map(|t| t.model.name)
            .collect::<HashSet<_>>()
    );
}

#[test]
fn list_tokens_exclude_revoked() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    let tokens = app.db(|conn| {
        vec![
            assert_ok!(ApiToken::insert(conn, id, "bar")),
            assert_ok!(ApiToken::insert(conn, id, "baz")),
        ]
    });

    // List tokens expecting them all to be there.
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len());

    // Revoke the first token.
    let _json: RevokedResponse = user
        .delete(&format!("/api/v1/me/tokens/{}", tokens[0].model.id))
        .good();

    // Check that we now have one less token being listed.
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len() - 1);
    assert!(json
        .api_tokens
        .iter()
        .find(|token| token.name == tokens[0].model.name)
        .is_none());
}

#[test]
fn create_token_logged_out() {
    let (_, anon) = TestApp::init().empty();
    anon.put(URL, NEW_BAR).assert_forbidden();
}

#[test]
fn create_token_invalid_request() {
    let (_, _, user) = TestApp::init().with_user();
    let invalid = br#"{ "name": "" }"#;
    let response = user.put::<()>(URL, invalid);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "invalid new token request: Error(\"missing field `api_token`\", line: 1, column: 14)" }] })
    );
}

#[test]
fn create_token_no_name() {
    let (_, _, user) = TestApp::init().with_user();
    let empty_name = br#"{ "api_token": { "name": "" } }"#;
    let response = user.put::<()>(URL, empty_name);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "name must have a value" }] })
    );
}

#[test]
fn create_token_long_body() {
    let (_, _, user) = TestApp::init().with_user();
    let too_big = &[5; 5192]; // Send a request with a 5kB body of 5's
    let response = user.put::<()>(URL, too_big);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "max content length is: 2000" }] })
    );
}

#[test]
fn create_token_exceeded_tokens_per_user() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    app.db(|conn| {
        for i in 0..1000 {
            assert_ok!(ApiToken::insert(conn, id, &format!("token {}", i)));
        }
    });
    let response = user.put::<()>(URL, NEW_BAR);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "maximum tokens per user is: 500" }] })
    );
}

#[test]
fn create_token_success() {
    let (app, _, user) = TestApp::init().with_user();

    let json: NewResponse = user.put(URL, NEW_BAR).good();
    assert_eq!(json.api_token.name, "bar");
    assert!(!json.api_token.token.is_empty());

    let tokens: Vec<ApiToken> =
        app.db(|conn| assert_ok!(ApiToken::belonging_to(user.as_model()).load(conn)));
    assert_eq!(tokens.len(), 1);
    assert_eq!(tokens[0].name, "bar");
    assert_eq!(tokens[0].revoked, false);
    assert_eq!(tokens[0].last_used_at, None);
}

#[test]
fn create_token_multiple_have_different_values() {
    let (_, _, user) = TestApp::init().with_user();
    let first: NewResponse = user.put(URL, NEW_BAR).good();
    let second: NewResponse = user.put(URL, NEW_BAR).good();

    assert_ne!(first.api_token.token, second.api_token.token);
}

#[test]
fn create_token_multiple_users_have_different_values() {
    let (app, _, user1) = TestApp::init().with_user();
    let first_token: NewResponse = user1.put(URL, NEW_BAR).good();

    let user2 = app.db_new_user("bar");
    let second_token: NewResponse = user2.put(URL, NEW_BAR).good();

    assert_ne!(first_token.api_token.token, second_token.api_token.token);
}

#[test]
fn cannot_create_token_with_token() {
    let (_, _, _, token) = TestApp::init().with_token();
    let response = token.put::<()>(
        "/api/v1/me/tokens",
        br#"{ "api_token": { "name": "baz" } }"#,
    );
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "cannot use an API token to create a new API token" }] })
    );
}

#[test]
fn revoke_token_non_existing() {
    let (_, _, user) = TestApp::init().with_user();
    let _json: RevokedResponse = user.delete("/api/v1/me/tokens/5").good();
}

#[test]
fn revoke_token_doesnt_revoke_other_users_token() {
    let (app, _, user1, token) = TestApp::init().with_token();
    let user1 = user1.as_model();
    let token = token.as_model();
    let user2 = app.db_new_user("baz");

    // List tokens for first user contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user1).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.name);
    });

    // Try revoke the token as second user
    let _json: RevokedResponse = user2
        .delete(&format!("/api/v1/me/tokens/{}", token.id))
        .good();

    // List tokens for first user still contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user1).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.name);
    });
}

#[test]
fn revoke_token_success() {
    let (app, _, user, token) = TestApp::init().with_token();

    // List tokens contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user.as_model()).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.as_model().name);
    });

    // Revoke the token
    let _json: RevokedResponse = user
        .delete(&format!("/api/v1/me/tokens/{}", token.as_model().id))
        .good();

    // List tokens no longer contains the token
    app.db(|conn| {
        let count = ApiToken::belonging_to(user.as_model())
            .filter(api_tokens::revoked.eq(false))
            .count()
            .get_result(conn);
        assert_eq!(count, Ok(0));
    });
}

#[test]
fn token_gives_access_to_me() {
    let url = "/api/v1/me";
    let (_, anon, user, token) = TestApp::init().with_token();

    anon.get(url).assert_forbidden();

    let json: UserShowPrivateResponse = token.get(url).good();
    assert_eq!(json.user.name, user.as_model().name);
}

#[test]
fn using_token_updates_last_used_at() {
    let url = "/api/v1/me";
    let (app, anon, user, token) = TestApp::init().with_token();

    anon.get(url).assert_forbidden();
    user.get::<EncodableMe>(url).good();
    assert_none!(token.as_model().last_used_at);

    // Use the token once
    token.get::<EncodableMe>("/api/v1/me").good();

    let token: ApiToken =
        app.db(|conn| assert_ok!(ApiToken::belonging_to(user.as_model()).first(conn)));
    assert_some!(token.last_used_at);

    // Would check that it updates the timestamp here, but the timestamp is
    // based on the start of the database transaction so it doesn't work in
    // this test framework.
}

#[test]
fn old_tokens_give_specific_error_message() {
    let url = "/api/v1/me";
    let (_, anon) = TestApp::init().empty();

    let mut request = anon.get_request(url);
    request.header(header::AUTHORIZATION, "oldtoken");
    let response = anon.run::<()>(request);
    response.assert_status(StatusCode::UNAUTHORIZED);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": TOKEN_FORMAT_ERROR }] })
    );
}
<commit_msg>tests: Remove unused `assert_contains!` macro<commit_after>use crate::{
    user::UserShowPrivateResponse,
    util::{header, StatusCode},
    RequestHelper, TestApp,
};
use cargo_registry::{
    models::ApiToken,
    schema::api_tokens,
    util::errors::TOKEN_FORMAT_ERROR,
    views::{EncodableApiTokenWithToken, EncodableMe},
};
use std::collections::HashSet;

use diesel::prelude::*;

#[derive(Deserialize)]
struct DecodableApiToken {
    name: String,
}

#[derive(Deserialize)]
struct ListResponse {
    api_tokens: Vec<DecodableApiToken>,
}
#[derive(Deserialize)]
struct NewResponse {
    api_token: EncodableApiTokenWithToken,
}
#[derive(Deserialize)]
struct RevokedResponse {}

// Default values used by many tests
static URL: &str = "/api/v1/me/tokens";
static NEW_BAR: &[u8] = br#"{ "api_token": { "name": "bar" } }"#;

#[test]
fn list_logged_out() {
    let (_, anon) = TestApp::init().empty();
    anon.get(URL).assert_forbidden();
}

#[test]
fn list_empty() {
    let (_, _, user) = TestApp::init().with_user();
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), 0);
}

#[test]
fn list_tokens() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    let tokens = app.db(|conn| {
        vec![
            assert_ok!(ApiToken::insert(conn, id, "bar")),
            assert_ok!(ApiToken::insert(conn, id, "baz")),
        ]
    });

    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len());
    assert_eq!(
        json.api_tokens
            .into_iter()
            .map(|t| t.name)
            .collect::<HashSet<_>>(),
        tokens
            .into_iter()
            .map(|t| t.model.name)
            .collect::<HashSet<_>>()
    );
}

#[test]
fn list_tokens_exclude_revoked() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    let tokens = app.db(|conn| {
        vec![
            assert_ok!(ApiToken::insert(conn, id, "bar")),
            assert_ok!(ApiToken::insert(conn, id, "baz")),
        ]
    });

    // List tokens expecting them all to be there.
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len());

    // Revoke the first token.
    let _json: RevokedResponse = user
        .delete(&format!("/api/v1/me/tokens/{}", tokens[0].model.id))
        .good();

    // Check that we now have one less token being listed.
    let json: ListResponse = user.get(URL).good();
    assert_eq!(json.api_tokens.len(), tokens.len() - 1);
    assert!(json
        .api_tokens
        .iter()
        .find(|token| token.name == tokens[0].model.name)
        .is_none());
}

#[test]
fn create_token_logged_out() {
    let (_, anon) = TestApp::init().empty();
    anon.put(URL, NEW_BAR).assert_forbidden();
}

#[test]
fn create_token_invalid_request() {
    let (_, _, user) = TestApp::init().with_user();
    let invalid = br#"{ "name": "" }"#;
    let response = user.put::<()>(URL, invalid);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "invalid new token request: Error(\"missing field `api_token`\", line: 1, column: 14)" }] })
    );
}

#[test]
fn create_token_no_name() {
    let (_, _, user) = TestApp::init().with_user();
    let empty_name = br#"{ "api_token": { "name": "" } }"#;
    let response = user.put::<()>(URL, empty_name);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "name must have a value" }] })
    );
}

#[test]
fn create_token_long_body() {
    let (_, _, user) = TestApp::init().with_user();
    let too_big = &[5; 5192]; // Send a request with a 5kB body of 5's
    let response = user.put::<()>(URL, too_big);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "max content length is: 2000" }] })
    );
}

#[test]
fn create_token_exceeded_tokens_per_user() {
    let (app, _, user) = TestApp::init().with_user();
    let id = user.as_model().id;
    app.db(|conn| {
        for i in 0..1000 {
            assert_ok!(ApiToken::insert(conn, id, &format!("token {}", i)));
        }
    });
    let response = user.put::<()>(URL, NEW_BAR);
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "maximum tokens per user is: 500" }] })
    );
}

#[test]
fn create_token_success() {
    let (app, _, user) = TestApp::init().with_user();

    let json: NewResponse = user.put(URL, NEW_BAR).good();
    assert_eq!(json.api_token.name, "bar");
    assert!(!json.api_token.token.is_empty());

    let tokens: Vec<ApiToken> =
        app.db(|conn| assert_ok!(ApiToken::belonging_to(user.as_model()).load(conn)));
    assert_eq!(tokens.len(), 1);
    assert_eq!(tokens[0].name, "bar");
    assert_eq!(tokens[0].revoked, false);
    assert_eq!(tokens[0].last_used_at, None);
}

#[test]
fn create_token_multiple_have_different_values() {
    let (_, _, user) = TestApp::init().with_user();
    let first: NewResponse = user.put(URL, NEW_BAR).good();
    let second: NewResponse = user.put(URL, NEW_BAR).good();

    assert_ne!(first.api_token.token, second.api_token.token);
}

#[test]
fn create_token_multiple_users_have_different_values() {
    let (app, _, user1) = TestApp::init().with_user();
    let first_token: NewResponse = user1.put(URL, NEW_BAR).good();

    let user2 = app.db_new_user("bar");
    let second_token: NewResponse = user2.put(URL, NEW_BAR).good();

    assert_ne!(first_token.api_token.token, second_token.api_token.token);
}

#[test]
fn cannot_create_token_with_token() {
    let (_, _, _, token) = TestApp::init().with_token();
    let response = token.put::<()>(
        "/api/v1/me/tokens",
        br#"{ "api_token": { "name": "baz" } }"#,
    );
    response.assert_status(StatusCode::BAD_REQUEST);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": "cannot use an API token to create a new API token" }] })
    );
}

#[test]
fn revoke_token_non_existing() {
    let (_, _, user) = TestApp::init().with_user();
    let _json: RevokedResponse = user.delete("/api/v1/me/tokens/5").good();
}

#[test]
fn revoke_token_doesnt_revoke_other_users_token() {
    let (app, _, user1, token) = TestApp::init().with_token();
    let user1 = user1.as_model();
    let token = token.as_model();
    let user2 = app.db_new_user("baz");

    // List tokens for first user contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user1).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.name);
    });

    // Try revoke the token as second user
    let _json: RevokedResponse = user2
        .delete(&format!("/api/v1/me/tokens/{}", token.id))
        .good();

    // List tokens for first user still contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user1).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.name);
    });
}

#[test]
fn revoke_token_success() {
    let (app, _, user, token) = TestApp::init().with_token();

    // List tokens contains the token
    app.db(|conn| {
        let tokens: Vec<ApiToken> = assert_ok!(ApiToken::belonging_to(user.as_model()).load(conn));
        assert_eq!(tokens.len(), 1);
        assert_eq!(tokens[0].name, token.as_model().name);
    });

    // Revoke the token
    let _json: RevokedResponse = user
        .delete(&format!("/api/v1/me/tokens/{}", token.as_model().id))
        .good();

    // List tokens no longer contains the token
    app.db(|conn| {
        let count = ApiToken::belonging_to(user.as_model())
            .filter(api_tokens::revoked.eq(false))
            .count()
            .get_result(conn);
        assert_eq!(count, Ok(0));
    });
}

#[test]
fn token_gives_access_to_me() {
    let url = "/api/v1/me";
    let (_, anon, user, token) = TestApp::init().with_token();

    anon.get(url).assert_forbidden();

    let json: UserShowPrivateResponse = token.get(url).good();
    assert_eq!(json.user.name, user.as_model().name);
}

#[test]
fn using_token_updates_last_used_at() {
    let url = "/api/v1/me";
    let (app, anon, user, token) = TestApp::init().with_token();

    anon.get(url).assert_forbidden();
    user.get::<EncodableMe>(url).good();
    assert_none!(token.as_model().last_used_at);

    // Use the token once
    token.get::<EncodableMe>("/api/v1/me").good();

    let token: ApiToken =
        app.db(|conn| assert_ok!(ApiToken::belonging_to(user.as_model()).first(conn)));
    assert_some!(token.last_used_at);

    // Would check that it updates the timestamp here, but the timestamp is
    // based on the start of the database transaction so it doesn't work in
    // this test framework.
}

#[test]
fn old_tokens_give_specific_error_message() {
    let url = "/api/v1/me";
    let (_, anon) = TestApp::init().empty();

    let mut request = anon.get_request(url);
    request.header(header::AUTHORIZATION, "oldtoken");
    let response = anon.run::<()>(request);
    response.assert_status(StatusCode::UNAUTHORIZED);
    assert_eq!(
        response.json(),
        json!({ "errors": [{ "detail": TOKEN_FORMAT_ERROR }] })
    );
}
<|endoftext|>


<commit_before>use llvm_sys::core::LLVMGetTypeKind;
use llvm_sys::LLVMTypeKind;
use llvm_sys::prelude::LLVMTypeRef;

use crate::types::{IntType, VoidType, FunctionType, PointerType, VectorType, ArrayType, StructType, FloatType};
use crate::types::traits::AsTypeRef;
use crate::values::{BasicValue, BasicValueEnum, IntValue};

use std::convert::TryFrom;

macro_rules! enum_type_set {
    ($(#[$enum_attrs:meta])* $enum_name:ident: { $($(#[$variant_attrs:meta])* $args:ident,)+ }) => (
        #[derive(Debug, PartialEq, Eq, Clone, Copy)]
        $(#[$enum_attrs])*
        pub enum $enum_name<'ctx> {
            $(
                $(#[$variant_attrs])*
                $args($args<'ctx>),
            )*
        }

        impl AsTypeRef for $enum_name<'_> {
            fn as_type_ref(&self) -> LLVMTypeRef {
                match *self {
                    $(
                        $enum_name::$args(ref t) => t.as_type_ref(),
                    )*
                }
            }
        }

        $(
            impl<'ctx> From<$args<'ctx>> for $enum_name<'ctx> {
                fn from(value: $args) -> $enum_name {
                    $enum_name::$args(value)
                }
            }

            impl<'ctx> TryFrom<$enum_name<'ctx>> for $args<'ctx> {
                type Error = ();

                fn try_from(value: $enum_name<'ctx>) -> Result<Self, Self::Error> {
                    match value {
                        $enum_name::$args(ty) => Ok(ty),
                        _ => Err(()),
                    }
                }
            }
        )*
    );
}

enum_type_set! {
    /// A wrapper for any `BasicType`, `VoidType`, or `FunctionType`.
    AnyTypeEnum: {
        /// A contiguous homogeneous container type.
        ArrayType,
        /// A floating point type.
        FloatType,
        /// A function return and parameter definition.
        FunctionType,
        /// An integer type.
        IntType,
        /// A pointer type.
        PointerType,
        /// A contiguous heterogeneous container type.
        StructType,
        /// A contiguous homogeneous "SIMD" container type.
        VectorType,
        /// A valueless type.
        VoidType,
    }
}
enum_type_set! {
    /// A wrapper for any `BasicType`.
    BasicTypeEnum: {
        /// A contiguous homogeneous container type.
        ArrayType,
        /// A floating point type.
        FloatType,
        /// An integer type.
        IntType,
        /// A pointer type.
        PointerType,
        /// A contiguous heterogeneous container type.
        StructType,
        /// A contiguous homogeneous "SIMD" container type.
        VectorType,
    }
}

impl<'ctx> AnyTypeEnum<'ctx> {
    pub(crate) unsafe fn new(type_: LLVMTypeRef) -> Self {
        match LLVMGetTypeKind(type_) {
            LLVMTypeKind::LLVMVoidTypeKind => AnyTypeEnum::VoidType(VoidType::new(type_)),
            LLVMTypeKind::LLVMHalfTypeKind |
            LLVMTypeKind::LLVMFloatTypeKind |
            LLVMTypeKind::LLVMDoubleTypeKind |
            LLVMTypeKind::LLVMX86_FP80TypeKind |
            LLVMTypeKind::LLVMFP128TypeKind |
            LLVMTypeKind::LLVMPPC_FP128TypeKind => AnyTypeEnum::FloatType(FloatType::new(type_)),
            #[cfg(feature = "llvm11-0")]
            LLVMTypeKind::LLVMBFloatTypeKind => AnyTypeEnum::FloatType(FloatType::new(type_)),
            LLVMTypeKind::LLVMLabelTypeKind => panic!("FIXME: Unsupported type: Label"),
            LLVMTypeKind::LLVMIntegerTypeKind => AnyTypeEnum::IntType(IntType::new(type_)),
            LLVMTypeKind::LLVMFunctionTypeKind => AnyTypeEnum::FunctionType(FunctionType::new(type_)),
            LLVMTypeKind::LLVMStructTypeKind => AnyTypeEnum::StructType(StructType::new(type_)),
            LLVMTypeKind::LLVMArrayTypeKind => AnyTypeEnum::ArrayType(ArrayType::new(type_)),
            LLVMTypeKind::LLVMPointerTypeKind => AnyTypeEnum::PointerType(PointerType::new(type_)),
            LLVMTypeKind::LLVMVectorTypeKind => AnyTypeEnum::VectorType(VectorType::new(type_)),
            #[cfg(feature = "llvm11-0")]
            LLVMTypeKind::LLVMScalableVectorTypeKind => AnyTypeEnum::VectorType(VectorType::new(type_)),
            LLVMTypeKind::LLVMMetadataTypeKind => panic!("FIXME: Unsupported type: Metadata"),
            LLVMTypeKind::LLVMX86_MMXTypeKind => panic!("FIXME: Unsupported type: MMX"),
            #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7")))]
            LLVMTypeKind::LLVMTokenTypeKind => panic!("FIXME: Unsupported type: Token"),
        }
    }

    /// This will panic if type is a void or function type.
    pub(crate) fn to_basic_type_enum(&self) -> BasicTypeEnum<'ctx> {
        unsafe {
            BasicTypeEnum::new(self.as_type_ref())
        }
    }

    pub fn into_array_type(self) -> ArrayType<'ctx> {
        if let AnyTypeEnum::ArrayType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_float_type(self) -> FloatType<'ctx> {
        if let AnyTypeEnum::FloatType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_function_type(self) -> FunctionType<'ctx> {
        if let AnyTypeEnum::FunctionType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_int_type(self) -> IntType<'ctx> {
        if let AnyTypeEnum::IntType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_pointer_type(self) -> PointerType<'ctx> {
        if let AnyTypeEnum::PointerType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_struct_type(self) -> StructType<'ctx> {
        if let AnyTypeEnum::StructType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_vector_type(self) -> VectorType<'ctx> {
        if let AnyTypeEnum::VectorType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_void_type(self) -> VoidType<'ctx> {
        if let AnyTypeEnum::VoidType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn is_array_type(self) -> bool {
        matches!(self, AnyTypeEnum::ArrayType(_))
    }

    pub fn is_float_type(self) -> bool {
        matches!(self, AnyTypeEnum::FloatType(_))
    }

    pub fn is_function_type(self) -> bool {
        matches!(self, AnyTypeEnum::FunctionType(_))
    }

    pub fn is_int_type(self) -> bool {
        matches!(self, AnyTypeEnum::IntType(_))
    }

    pub fn is_pointer_type(self) -> bool {
        matches!(self, AnyTypeEnum::PointerType(_))
    }

    pub fn is_struct_type(self) -> bool {
        matches!(self, AnyTypeEnum::StructType(_))
    }

    pub fn is_vector_type(self) -> bool {
        matches!(self, AnyTypeEnum::VectorType(_))
    }

    pub fn is_void_type(self) -> bool {
        matches!(self, AnyTypeEnum::VoidType(_))
    }

    pub fn size_of(&self) -> Option<IntValue<'ctx>> {
        match self {
            AnyTypeEnum::ArrayType(t) => t.size_of(),
            AnyTypeEnum::FloatType(t) => Some(t.size_of()),
            AnyTypeEnum::IntType(t) => Some(t.size_of()),
            AnyTypeEnum::PointerType(t) => Some(t.size_of()),
            AnyTypeEnum::StructType(t) => t.size_of(),
            AnyTypeEnum::VectorType(t) => t.size_of(),
            AnyTypeEnum::VoidType(_) => None,
            AnyTypeEnum::FunctionType(_) => None,
        }
    }
}

impl<'ctx> BasicTypeEnum<'ctx> {
    pub(crate) unsafe fn new(type_: LLVMTypeRef) -> Self {
        match LLVMGetTypeKind(type_) {
            LLVMTypeKind::LLVMHalfTypeKind |
            LLVMTypeKind::LLVMFloatTypeKind |
            LLVMTypeKind::LLVMDoubleTypeKind |
            LLVMTypeKind::LLVMX86_FP80TypeKind |
            LLVMTypeKind::LLVMFP128TypeKind |
            LLVMTypeKind::LLVMPPC_FP128TypeKind => BasicTypeEnum::FloatType(FloatType::new(type_)),
            #[cfg(feature= "llvm11-0")]
            LLVMTypeKind::LLVMBFloatTypeKind => BasicTypeEnum::FloatType(FloatType::new(type_)),
            LLVMTypeKind::LLVMIntegerTypeKind => BasicTypeEnum::IntType(IntType::new(type_)),
            LLVMTypeKind::LLVMStructTypeKind => BasicTypeEnum::StructType(StructType::new(type_)),
            LLVMTypeKind::LLVMPointerTypeKind => BasicTypeEnum::PointerType(PointerType::new(type_)),
            LLVMTypeKind::LLVMArrayTypeKind => BasicTypeEnum::ArrayType(ArrayType::new(type_)),
            LLVMTypeKind::LLVMVectorTypeKind => BasicTypeEnum::VectorType(VectorType::new(type_)),
            #[cfg(feature= "llvm11-0")]
            LLVMTypeKind::LLVMScalableVectorTypeKind => BasicTypeEnum::VectorType(VectorType::new(type_)),
            LLVMTypeKind::LLVMMetadataTypeKind => unreachable!("Unsupported basic type: Metadata"),
            LLVMTypeKind::LLVMX86_MMXTypeKind => unreachable!("Unsupported basic type: MMX"),
            LLVMTypeKind::LLVMLabelTypeKind => unreachable!("Unsupported basic type: Label"),
            LLVMTypeKind::LLVMVoidTypeKind => unreachable!("Unsupported basic type: VoidType"),
            LLVMTypeKind::LLVMFunctionTypeKind => unreachable!("Unsupported basic type: FunctionType"),
            #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7")))]
            LLVMTypeKind::LLVMTokenTypeKind => unreachable!("Unsupported basic type: Token"),
        }
    }

    pub fn into_array_type(self) -> ArrayType<'ctx> {
        if let BasicTypeEnum::ArrayType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_float_type(self) -> FloatType<'ctx> {
        if let BasicTypeEnum::FloatType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_int_type(self) -> IntType<'ctx> {
        if let BasicTypeEnum::IntType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_pointer_type(self) -> PointerType<'ctx> {
        if let BasicTypeEnum::PointerType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_struct_type(self) -> StructType<'ctx> {
        if let BasicTypeEnum::StructType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn into_vector_type(self) -> VectorType<'ctx> {
        if let BasicTypeEnum::VectorType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected another variant", self);
        }
    }

    pub fn is_array_type(self) -> bool {
        matches!(self, BasicTypeEnum::ArrayType(_))
    }

    pub fn is_float_type(self) -> bool {
        matches!(self, BasicTypeEnum::FloatType(_))
    }

    pub fn is_int_type(self) -> bool {
        matches!(self, BasicTypeEnum::IntType(_))
    }

    pub fn is_pointer_type(self) -> bool {
        matches!(self, BasicTypeEnum::PointerType(_))
    }

    pub fn is_struct_type(self) -> bool {
        matches!(self, BasicTypeEnum::StructType(_))
    }

    pub fn is_vector_type(self) -> bool {
        matches!(self, BasicTypeEnum::VectorType(_))
    }

    /// Creates a constant `BasicValueZero`.
    ///
    /// # Example
    /// ```
    /// use inkwell::context::Context;
    /// use crate::inkwell::types::BasicType;
    ///
    /// let context = Context::create();
    /// let f32_type = context.f32_type().as_basic_type_enum();
    /// let f32_zero = f32_type.const_zero();
    /// ```
    pub fn const_zero(self) -> BasicValueEnum<'ctx> {
        match self {
            BasicTypeEnum::ArrayType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::FloatType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::IntType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::PointerType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::StructType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::VectorType(ty) => ty.const_zero().as_basic_value_enum(),
        }
    }
}

impl<'ctx> TryFrom<AnyTypeEnum<'ctx>> for BasicTypeEnum<'ctx> {
    type Error = ();

    fn try_from(value: AnyTypeEnum<'ctx>) -> Result<Self, Self::Error> {
        Ok(match value {
            AnyTypeEnum::ArrayType(at) => at.into(),
            AnyTypeEnum::FloatType(ft) => ft.into(),
            AnyTypeEnum::IntType(it) => it.into(),
            AnyTypeEnum::PointerType(pt) => pt.into(),
            AnyTypeEnum::StructType(st) => st.into(),
            AnyTypeEnum::VectorType(vt) => vt.into(),
            _ => return Err(()),
        })
    }
}
<commit_msg>add more detailed error messages for type enum conversion<commit_after>use llvm_sys::core::LLVMGetTypeKind;
use llvm_sys::LLVMTypeKind;
use llvm_sys::prelude::LLVMTypeRef;

use crate::types::{IntType, VoidType, FunctionType, PointerType, VectorType, ArrayType, StructType, FloatType};
use crate::types::traits::AsTypeRef;
use crate::values::{BasicValue, BasicValueEnum, IntValue};

use std::convert::TryFrom;

macro_rules! enum_type_set {
    ($(#[$enum_attrs:meta])* $enum_name:ident: { $($(#[$variant_attrs:meta])* $args:ident,)+ }) => (
        #[derive(Debug, PartialEq, Eq, Clone, Copy)]
        $(#[$enum_attrs])*
        pub enum $enum_name<'ctx> {
            $(
                $(#[$variant_attrs])*
                $args($args<'ctx>),
            )*
        }

        impl AsTypeRef for $enum_name<'_> {
            fn as_type_ref(&self) -> LLVMTypeRef {
                match *self {
                    $(
                        $enum_name::$args(ref t) => t.as_type_ref(),
                    )*
                }
            }
        }

        $(
            impl<'ctx> From<$args<'ctx>> for $enum_name<'ctx> {
                fn from(value: $args) -> $enum_name {
                    $enum_name::$args(value)
                }
            }

            impl<'ctx> TryFrom<$enum_name<'ctx>> for $args<'ctx> {
                type Error = ();

                fn try_from(value: $enum_name<'ctx>) -> Result<Self, Self::Error> {
                    match value {
                        $enum_name::$args(ty) => Ok(ty),
                        _ => Err(()),
                    }
                }
            }
        )*
    );
}

enum_type_set! {
    /// A wrapper for any `BasicType`, `VoidType`, or `FunctionType`.
    AnyTypeEnum: {
        /// A contiguous homogeneous container type.
        ArrayType,
        /// A floating point type.
        FloatType,
        /// A function return and parameter definition.
        FunctionType,
        /// An integer type.
        IntType,
        /// A pointer type.
        PointerType,
        /// A contiguous heterogeneous container type.
        StructType,
        /// A contiguous homogeneous "SIMD" container type.
        VectorType,
        /// A valueless type.
        VoidType,
    }
}
enum_type_set! {
    /// A wrapper for any `BasicType`.
    BasicTypeEnum: {
        /// A contiguous homogeneous container type.
        ArrayType,
        /// A floating point type.
        FloatType,
        /// An integer type.
        IntType,
        /// A pointer type.
        PointerType,
        /// A contiguous heterogeneous container type.
        StructType,
        /// A contiguous homogeneous "SIMD" container type.
        VectorType,
    }
}

impl<'ctx> AnyTypeEnum<'ctx> {
    pub(crate) unsafe fn new(type_: LLVMTypeRef) -> Self {
        match LLVMGetTypeKind(type_) {
            LLVMTypeKind::LLVMVoidTypeKind => AnyTypeEnum::VoidType(VoidType::new(type_)),
            LLVMTypeKind::LLVMHalfTypeKind |
            LLVMTypeKind::LLVMFloatTypeKind |
            LLVMTypeKind::LLVMDoubleTypeKind |
            LLVMTypeKind::LLVMX86_FP80TypeKind |
            LLVMTypeKind::LLVMFP128TypeKind |
            LLVMTypeKind::LLVMPPC_FP128TypeKind => AnyTypeEnum::FloatType(FloatType::new(type_)),
            #[cfg(feature = "llvm11-0")]
            LLVMTypeKind::LLVMBFloatTypeKind => AnyTypeEnum::FloatType(FloatType::new(type_)),
            LLVMTypeKind::LLVMLabelTypeKind => panic!("FIXME: Unsupported type: Label"),
            LLVMTypeKind::LLVMIntegerTypeKind => AnyTypeEnum::IntType(IntType::new(type_)),
            LLVMTypeKind::LLVMFunctionTypeKind => AnyTypeEnum::FunctionType(FunctionType::new(type_)),
            LLVMTypeKind::LLVMStructTypeKind => AnyTypeEnum::StructType(StructType::new(type_)),
            LLVMTypeKind::LLVMArrayTypeKind => AnyTypeEnum::ArrayType(ArrayType::new(type_)),
            LLVMTypeKind::LLVMPointerTypeKind => AnyTypeEnum::PointerType(PointerType::new(type_)),
            LLVMTypeKind::LLVMVectorTypeKind => AnyTypeEnum::VectorType(VectorType::new(type_)),
            #[cfg(feature = "llvm11-0")]
            LLVMTypeKind::LLVMScalableVectorTypeKind => AnyTypeEnum::VectorType(VectorType::new(type_)),
            LLVMTypeKind::LLVMMetadataTypeKind => panic!("FIXME: Unsupported type: Metadata"),
            LLVMTypeKind::LLVMX86_MMXTypeKind => panic!("FIXME: Unsupported type: MMX"),
            #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7")))]
            LLVMTypeKind::LLVMTokenTypeKind => panic!("FIXME: Unsupported type: Token"),
        }
    }

    /// This will panic if type is a void or function type.
    pub(crate) fn to_basic_type_enum(&self) -> BasicTypeEnum<'ctx> {
        unsafe {
            BasicTypeEnum::new(self.as_type_ref())
        }
    }

    pub fn into_array_type(self) -> ArrayType<'ctx> {
        if let AnyTypeEnum::ArrayType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the ArrayType variant", self);
        }
    }

    pub fn into_float_type(self) -> FloatType<'ctx> {
        if let AnyTypeEnum::FloatType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the FloatType variant", self);
        }
    }

    pub fn into_function_type(self) -> FunctionType<'ctx> {
        if let AnyTypeEnum::FunctionType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the FunctionType variant", self);
        }
    }

    pub fn into_int_type(self) -> IntType<'ctx> {
        if let AnyTypeEnum::IntType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the IntType variant", self);
        }
    }

    pub fn into_pointer_type(self) -> PointerType<'ctx> {
        if let AnyTypeEnum::PointerType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the PointerType variant", self);
        }
    }

    pub fn into_struct_type(self) -> StructType<'ctx> {
        if let AnyTypeEnum::StructType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the StructType variant", self);
        }
    }

    pub fn into_vector_type(self) -> VectorType<'ctx> {
        if let AnyTypeEnum::VectorType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the VectorType variant", self);
        }
    }

    pub fn into_void_type(self) -> VoidType<'ctx> {
        if let AnyTypeEnum::VoidType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the VoidType variant", self);
        }
    }

    pub fn is_array_type(self) -> bool {
        matches!(self, AnyTypeEnum::ArrayType(_))
    }

    pub fn is_float_type(self) -> bool {
        matches!(self, AnyTypeEnum::FloatType(_))
    }

    pub fn is_function_type(self) -> bool {
        matches!(self, AnyTypeEnum::FunctionType(_))
    }

    pub fn is_int_type(self) -> bool {
        matches!(self, AnyTypeEnum::IntType(_))
    }

    pub fn is_pointer_type(self) -> bool {
        matches!(self, AnyTypeEnum::PointerType(_))
    }

    pub fn is_struct_type(self) -> bool {
        matches!(self, AnyTypeEnum::StructType(_))
    }

    pub fn is_vector_type(self) -> bool {
        matches!(self, AnyTypeEnum::VectorType(_))
    }

    pub fn is_void_type(self) -> bool {
        matches!(self, AnyTypeEnum::VoidType(_))
    }

    pub fn size_of(&self) -> Option<IntValue<'ctx>> {
        match self {
            AnyTypeEnum::ArrayType(t) => t.size_of(),
            AnyTypeEnum::FloatType(t) => Some(t.size_of()),
            AnyTypeEnum::IntType(t) => Some(t.size_of()),
            AnyTypeEnum::PointerType(t) => Some(t.size_of()),
            AnyTypeEnum::StructType(t) => t.size_of(),
            AnyTypeEnum::VectorType(t) => t.size_of(),
            AnyTypeEnum::VoidType(_) => None,
            AnyTypeEnum::FunctionType(_) => None,
        }
    }
}

impl<'ctx> BasicTypeEnum<'ctx> {
    pub(crate) unsafe fn new(type_: LLVMTypeRef) -> Self {
        match LLVMGetTypeKind(type_) {
            LLVMTypeKind::LLVMHalfTypeKind |
            LLVMTypeKind::LLVMFloatTypeKind |
            LLVMTypeKind::LLVMDoubleTypeKind |
            LLVMTypeKind::LLVMX86_FP80TypeKind |
            LLVMTypeKind::LLVMFP128TypeKind |
            LLVMTypeKind::LLVMPPC_FP128TypeKind => BasicTypeEnum::FloatType(FloatType::new(type_)),
            #[cfg(feature= "llvm11-0")]
            LLVMTypeKind::LLVMBFloatTypeKind => BasicTypeEnum::FloatType(FloatType::new(type_)),
            LLVMTypeKind::LLVMIntegerTypeKind => BasicTypeEnum::IntType(IntType::new(type_)),
            LLVMTypeKind::LLVMStructTypeKind => BasicTypeEnum::StructType(StructType::new(type_)),
            LLVMTypeKind::LLVMPointerTypeKind => BasicTypeEnum::PointerType(PointerType::new(type_)),
            LLVMTypeKind::LLVMArrayTypeKind => BasicTypeEnum::ArrayType(ArrayType::new(type_)),
            LLVMTypeKind::LLVMVectorTypeKind => BasicTypeEnum::VectorType(VectorType::new(type_)),
            #[cfg(feature= "llvm11-0")]
            LLVMTypeKind::LLVMScalableVectorTypeKind => BasicTypeEnum::VectorType(VectorType::new(type_)),
            LLVMTypeKind::LLVMMetadataTypeKind => unreachable!("Unsupported basic type: Metadata"),
            LLVMTypeKind::LLVMX86_MMXTypeKind => unreachable!("Unsupported basic type: MMX"),
            LLVMTypeKind::LLVMLabelTypeKind => unreachable!("Unsupported basic type: Label"),
            LLVMTypeKind::LLVMVoidTypeKind => unreachable!("Unsupported basic type: VoidType"),
            LLVMTypeKind::LLVMFunctionTypeKind => unreachable!("Unsupported basic type: FunctionType"),
            #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7")))]
            LLVMTypeKind::LLVMTokenTypeKind => unreachable!("Unsupported basic type: Token"),
        }
    }

    pub fn into_array_type(self) -> ArrayType<'ctx> {
        if let BasicTypeEnum::ArrayType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the ArrayType variant", self);
        }
    }

    pub fn into_float_type(self) -> FloatType<'ctx> {
        if let BasicTypeEnum::FloatType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the FloatType variant", self);
        }
    }

    pub fn into_int_type(self) -> IntType<'ctx> {
        if let BasicTypeEnum::IntType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the IntType variant", self);
        }
    }

    pub fn into_pointer_type(self) -> PointerType<'ctx> {
        if let BasicTypeEnum::PointerType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the PointerType variant", self);
        }
    }

    pub fn into_struct_type(self) -> StructType<'ctx> {
        if let BasicTypeEnum::StructType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the StructType variant", self);
        }
    }

    pub fn into_vector_type(self) -> VectorType<'ctx> {
        if let BasicTypeEnum::VectorType(t) = self {
            t
        } else {
            panic!("Found {:?} but expected the VectorType variant", self);
        }
    }

    pub fn is_array_type(self) -> bool {
        matches!(self, BasicTypeEnum::ArrayType(_))
    }

    pub fn is_float_type(self) -> bool {
        matches!(self, BasicTypeEnum::FloatType(_))
    }

    pub fn is_int_type(self) -> bool {
        matches!(self, BasicTypeEnum::IntType(_))
    }

    pub fn is_pointer_type(self) -> bool {
        matches!(self, BasicTypeEnum::PointerType(_))
    }

    pub fn is_struct_type(self) -> bool {
        matches!(self, BasicTypeEnum::StructType(_))
    }

    pub fn is_vector_type(self) -> bool {
        matches!(self, BasicTypeEnum::VectorType(_))
    }

    /// Creates a constant `BasicValueZero`.
    ///
    /// # Example
    /// ```
    /// use inkwell::context::Context;
    /// use crate::inkwell::types::BasicType;
    ///
    /// let context = Context::create();
    /// let f32_type = context.f32_type().as_basic_type_enum();
    /// let f32_zero = f32_type.const_zero();
    /// ```
    pub fn const_zero(self) -> BasicValueEnum<'ctx> {
        match self {
            BasicTypeEnum::ArrayType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::FloatType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::IntType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::PointerType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::StructType(ty) => ty.const_zero().as_basic_value_enum(),
            BasicTypeEnum::VectorType(ty) => ty.const_zero().as_basic_value_enum(),
        }
    }
}

impl<'ctx> TryFrom<AnyTypeEnum<'ctx>> for BasicTypeEnum<'ctx> {
    type Error = ();

    fn try_from(value: AnyTypeEnum<'ctx>) -> Result<Self, Self::Error> {
        Ok(match value {
            AnyTypeEnum::ArrayType(at) => at.into(),
            AnyTypeEnum::FloatType(ft) => ft.into(),
            AnyTypeEnum::IntType(it) => it.into(),
            AnyTypeEnum::PointerType(pt) => pt.into(),
            AnyTypeEnum::StructType(st) => st.into(),
            AnyTypeEnum::VectorType(vt) => vt.into(),
            _ => return Err(()),
        })
    }
}
<|endoftext|>