<commit_before>//! Models pertaining to the gateway.

use parking_lot::RwLock;
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
use serde_json;
use std::sync::Arc;
use super::utils::*;
use super::prelude::*;

/// A representation of the data retrieved from the bot gateway endpoint.
///
/// This is different from the [`Gateway`], as this includes the number of
/// shards that Discord recommends to use for a bot user.
///
/// This is only applicable to bot users.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BotGateway {
    /// The number of shards that is recommended to be used by the current bot
    /// user.
    pub shards: u64,
    /// The gateway to connect to.
    pub url: String,
}

/// Representation of a game that a [`User`] is playing -- or streaming in the
/// case that a stream URL is provided.
#[derive(Clone, Debug, Serialize)]
pub struct Game {
    /// The type of game status.
    #[serde(default, rename = "type")]
    pub kind: GameType,
    /// The name of the game being played.
    pub name: String,
    /// The Stream URL if [`kind`] is [`GameType::Streaming`].
    ///
    /// [`GameType::Streaming`]: enum.GameType.html#variant.Streaming
    /// [`kind`]: #structfield.kind
    pub url: Option<String>,
}

#[cfg(feature = "model")]
impl Game {
    /// Creates a `Game` struct that appears as a `Playing <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game being played:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// command!(game(ctx, _msg, args) {
    ///     let name = args.full();
    ///     ctx.set_game(Game::playing(&name));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn playing(name: &str) -> Game {
        Game {
            kind: GameType::Playing,
            name: name.to_string(),
            url: None,
        }
    }

    /// Creates a `Game` struct that appears as a `Streaming <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game and stream:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// // Assumes command has min_args set to 2.
    /// command!(stream(ctx, _msg, args) {
    ///     # let stream_url = String::from("");
    ///     let name = args.full();
    ///     ctx.set_game(Game::streaming(&name, &stream_url));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn streaming(name: &str, url: &str) -> Game {
        Game {
            kind: GameType::Streaming,
            name: name.to_string(),
            url: Some(url.to_string()),
        }
    }

    /// Creates a `Game` struct that appears as a `Listening to <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game being played:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// command!(listen(ctx, _msg, args) {
    ///     let name = args.full();
    ///     ctx.set_game(Game::listening(&name));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn listening(name: &str) -> Game {
        Game {
            kind: GameType::Listening,
            name: name.to_string(),
            url: None,
        }
    }

    /// Creates a `Game` struct that appears as a `Watching <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game being played:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// command!(watch(ctx, _msg, args) {
    ///     let name = args.full();
    ///     ctx.set_game(Game::watching(&name));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn watching(name: &str) -> Game {
        Game {
            kind: GameType::Watching,
            name: name.to_string(),
            url: None,
        }
    }
}

impl<'de> Deserialize<'de> for Game {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        let mut map = JsonMap::deserialize(deserializer)?;
        let kind = map.remove("type")
            .and_then(|v| GameType::deserialize(v).ok())
            .unwrap_or(GameType::Playing);
        let name = map.remove("name")
            .and_then(|v| String::deserialize(v).ok())
            .unwrap_or_else(String::new);
        let url = map.remove("url")
            .and_then(|v| serde_json::from_value::<String>(v).ok());

        Ok(Game {
            kind: kind,
            name: name,
            url: url,
        })
    }
}

enum_number!(
    /// The type of activity that is being performed when playing a game.
    GameType {
        /// An indicator that the user is playing a game.
        Playing = 0,
        /// An indicator that the user is streaming to a service.
        Streaming = 1,
        /// An indicator that the user is listening to something.
        Listening = 2,
        /// An indicator that the user is watching something.
        Watching = 3,
    }
);

impl GameType {
    pub fn num(&self) -> u64 {
        use self::GameType::*;

        match *self {
            Playing => 0,
            Streaming => 1,
            Listening => 2,
            Watching => 3,
        }
    }
}

impl Default for GameType {
    fn default() -> Self { GameType::Playing }
}

/// A representation of the data retrieved from the gateway endpoint.
///
/// For the bot-specific gateway, refer to [`BotGateway`].
///
/// [`BotGateway`]: struct.BotGateway.html
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Gateway {
    /// The gateway to connect to.
    pub url: String,
}

/// Information detailing the current online status of a [`User`].
///
/// [`User`]: struct.User.html
#[derive(Clone, Debug)]
pub struct Presence {
    /// The game that a [`User`] is current playing.
    ///
    /// [`User`]: struct.User.html
    pub game: Option<Game>,
    /// The date of the last presence update.
    pub last_modified: Option<u64>,
    /// The nickname of the member, if applicable.
    pub nick: Option<String>,
    /// The user's online status.
    pub status: OnlineStatus,
    /// The Id of the [`User`]. Can be used to calculate the user's creation
    /// date.
    pub user_id: UserId,
    /// The associated user instance.
    pub user: Option<Arc<RwLock<User>>>,
}

impl<'de> Deserialize<'de> for Presence {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Presence, D::Error> {
        let mut map = JsonMap::deserialize(deserializer)?;
        let mut user_map = map.remove("user")
            .ok_or_else(|| DeError::custom("expected presence user"))
            .and_then(JsonMap::deserialize)
            .map_err(DeError::custom)?;

        let (user_id, user) = if user_map.len() > 1 {
            let user = User::deserialize(Value::Object(user_map))
                .map_err(DeError::custom)?;

            (user.id, Some(Arc::new(RwLock::new(user))))
        } else {
            let user_id = user_map
                .remove("id")
                .ok_or_else(|| DeError::custom("Missing presence user id"))
                .and_then(|x| UserId::deserialize(x.clone()))
                .map_err(DeError::custom)?;

            (user_id, None)
        };

        let game = match map.remove("game") {
            Some(v) => serde_json::from_value::<Option<Game>>(v)
                .map_err(DeError::custom)?,
            None => None,
        };
        let last_modified = match map.remove("last_modified") {
            Some(v) => Some(u64::deserialize(v).map_err(DeError::custom)?),
            None => None,
        };
        let nick = match map.remove("nick") {
            Some(v) => serde_json::from_value::<Option<String>>(v)
                .map_err(DeError::custom)?,
            None => None,
        };
        let status = map.remove("status")
            .ok_or_else(|| DeError::custom("expected presence status"))
            .and_then(OnlineStatus::deserialize)
            .map_err(DeError::custom)?;

        Ok(Presence {
            game: game,
            last_modified: last_modified,
            nick: nick,
            status: status,
            user: user,
            user_id: user_id,
        })
    }
}

impl Serialize for Presence {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
        where S: Serializer {
        #[derive(Serialize)]
        struct UserId {
            id: u64,
        }

        let mut state = serializer.serialize_struct("Presence", 5)?;
        state.serialize_field("game", &self.game)?;
        state.serialize_field("last_modified", &self.last_modified)?;
        state.serialize_field("nick", &self.nick)?;
        state.serialize_field("status", &self.status)?;

        if let Some(ref user) = self.user {
            state.serialize_field("user", &*user.read())?;
        } else {
            state.serialize_field("user", &UserId {
                id: self.user_id.0,
            })?;
        }

        state.end()
    }
}

/// An initial set of information given after IDENTIFYing to the gateway.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Ready {
    pub guilds: Vec<GuildStatus>,
    #[serde(default, deserialize_with = "deserialize_presences")]
    pub presences: HashMap<UserId, Presence>,
    #[serde(default, deserialize_with = "deserialize_private_channels")]
    pub private_channels: HashMap<ChannelId, Channel>,
    pub session_id: String,
    pub shard: Option<[u64; 2]>,
    #[serde(default, rename = "_trace")]
    pub trace: Vec<String>,
    pub user: CurrentUser,
    #[serde(rename = "v")]
    pub version: u64,
}
<commit_msg>Remove GameType::Watching<commit_after>//! Models pertaining to the gateway.

use parking_lot::RwLock;
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
use serde_json;
use std::sync::Arc;
use super::utils::*;
use super::prelude::*;

/// A representation of the data retrieved from the bot gateway endpoint.
///
/// This is different from the [`Gateway`], as this includes the number of
/// shards that Discord recommends to use for a bot user.
///
/// This is only applicable to bot users.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BotGateway {
    /// The number of shards that is recommended to be used by the current bot
    /// user.
    pub shards: u64,
    /// The gateway to connect to.
    pub url: String,
}

/// Representation of a game that a [`User`] is playing -- or streaming in the
/// case that a stream URL is provided.
#[derive(Clone, Debug, Serialize)]
pub struct Game {
    /// The type of game status.
    #[serde(default, rename = "type")]
    pub kind: GameType,
    /// The name of the game being played.
    pub name: String,
    /// The Stream URL if [`kind`] is [`GameType::Streaming`].
    ///
    /// [`GameType::Streaming`]: enum.GameType.html#variant.Streaming
    /// [`kind`]: #structfield.kind
    pub url: Option<String>,
}

#[cfg(feature = "model")]
impl Game {
    /// Creates a `Game` struct that appears as a `Playing <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game being played:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// command!(game(ctx, _msg, args) {
    ///     let name = args.full();
    ///     ctx.set_game(Game::playing(&name));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn playing(name: &str) -> Game {
        Game {
            kind: GameType::Playing,
            name: name.to_string(),
            url: None,
        }
    }

    /// Creates a `Game` struct that appears as a `Streaming <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game and stream:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// // Assumes command has min_args set to 2.
    /// command!(stream(ctx, _msg, args) {
    ///     # let stream_url = String::from("");
    ///     let name = args.full();
    ///     ctx.set_game(Game::streaming(&name, &stream_url));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn streaming(name: &str, url: &str) -> Game {
        Game {
            kind: GameType::Streaming,
            name: name.to_string(),
            url: Some(url.to_string()),
        }
    }

    /// Creates a `Game` struct that appears as a `Listening to <name>` status.
    ///
    /// **Note**: Maximum `name` length is 128.
    ///
    /// # Examples
    ///
    /// Create a command that sets the current game being played:
    ///
    /// ```rust,no_run
    /// # #[macro_use] extern crate serenity;
    /// #
    /// use serenity::framework::standard::Args;
    /// use serenity::model::gateway::Game;
    ///
    /// command!(listen(ctx, _msg, args) {
    ///     let name = args.full();
    ///     ctx.set_game(Game::listening(&name));
    /// });
    /// #
    /// # fn main() {}
    /// ```
    pub fn listening(name: &str) -> Game {
        Game {
            kind: GameType::Listening,
            name: name.to_string(),
            url: None,
        }
    }
}

impl<'de> Deserialize<'de> for Game {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        let mut map = JsonMap::deserialize(deserializer)?;
        let kind = map.remove("type")
            .and_then(|v| GameType::deserialize(v).ok())
            .unwrap_or(GameType::Playing);
        let name = map.remove("name")
            .and_then(|v| String::deserialize(v).ok())
            .unwrap_or_else(String::new);
        let url = map.remove("url")
            .and_then(|v| serde_json::from_value::<String>(v).ok());

        Ok(Game {
            kind: kind,
            name: name,
            url: url,
        })
    }
}

enum_number!(
    /// The type of activity that is being performed when playing a game.
    GameType {
        /// An indicator that the user is playing a game.
        Playing = 0,
        /// An indicator that the user is streaming to a service.
        Streaming = 1,
        /// An indicator that the user is listening to something.
        Listening = 2,
    }
);

impl GameType {
    pub fn num(&self) -> u64 {
        use self::GameType::*;

        match *self {
            Playing => 0,
            Streaming => 1,
            Listening => 2,
        }
    }
}

impl Default for GameType {
    fn default() -> Self { GameType::Playing }
}

/// A representation of the data retrieved from the gateway endpoint.
///
/// For the bot-specific gateway, refer to [`BotGateway`].
///
/// [`BotGateway`]: struct.BotGateway.html
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Gateway {
    /// The gateway to connect to.
    pub url: String,
}

/// Information detailing the current online status of a [`User`].
///
/// [`User`]: struct.User.html
#[derive(Clone, Debug)]
pub struct Presence {
    /// The game that a [`User`] is current playing.
    ///
    /// [`User`]: struct.User.html
    pub game: Option<Game>,
    /// The date of the last presence update.
    pub last_modified: Option<u64>,
    /// The nickname of the member, if applicable.
    pub nick: Option<String>,
    /// The user's online status.
    pub status: OnlineStatus,
    /// The Id of the [`User`]. Can be used to calculate the user's creation
    /// date.
    pub user_id: UserId,
    /// The associated user instance.
    pub user: Option<Arc<RwLock<User>>>,
}

impl<'de> Deserialize<'de> for Presence {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Presence, D::Error> {
        let mut map = JsonMap::deserialize(deserializer)?;
        let mut user_map = map.remove("user")
            .ok_or_else(|| DeError::custom("expected presence user"))
            .and_then(JsonMap::deserialize)
            .map_err(DeError::custom)?;

        let (user_id, user) = if user_map.len() > 1 {
            let user = User::deserialize(Value::Object(user_map))
                .map_err(DeError::custom)?;

            (user.id, Some(Arc::new(RwLock::new(user))))
        } else {
            let user_id = user_map
                .remove("id")
                .ok_or_else(|| DeError::custom("Missing presence user id"))
                .and_then(|x| UserId::deserialize(x.clone()))
                .map_err(DeError::custom)?;

            (user_id, None)
        };

        let game = match map.remove("game") {
            Some(v) => serde_json::from_value::<Option<Game>>(v)
                .map_err(DeError::custom)?,
            None => None,
        };
        let last_modified = match map.remove("last_modified") {
            Some(v) => Some(u64::deserialize(v).map_err(DeError::custom)?),
            None => None,
        };
        let nick = match map.remove("nick") {
            Some(v) => serde_json::from_value::<Option<String>>(v)
                .map_err(DeError::custom)?,
            None => None,
        };
        let status = map.remove("status")
            .ok_or_else(|| DeError::custom("expected presence status"))
            .and_then(OnlineStatus::deserialize)
            .map_err(DeError::custom)?;

        Ok(Presence {
            game: game,
            last_modified: last_modified,
            nick: nick,
            status: status,
            user: user,
            user_id: user_id,
        })
    }
}

impl Serialize for Presence {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
        where S: Serializer {
        #[derive(Serialize)]
        struct UserId {
            id: u64,
        }

        let mut state = serializer.serialize_struct("Presence", 5)?;
        state.serialize_field("game", &self.game)?;
        state.serialize_field("last_modified", &self.last_modified)?;
        state.serialize_field("nick", &self.nick)?;
        state.serialize_field("status", &self.status)?;

        if let Some(ref user) = self.user {
            state.serialize_field("user", &*user.read())?;
        } else {
            state.serialize_field("user", &UserId {
                id: self.user_id.0,
            })?;
        }

        state.end()
    }
}

/// An initial set of information given after IDENTIFYing to the gateway.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Ready {
    pub guilds: Vec<GuildStatus>,
    #[serde(default, deserialize_with = "deserialize_presences")]
    pub presences: HashMap<UserId, Presence>,
    #[serde(default, deserialize_with = "deserialize_private_channels")]
    pub private_channels: HashMap<ChannelId, Channel>,
    pub session_id: String,
    pub shard: Option<[u64; 2]>,
    #[serde(default, rename = "_trace")]
    pub trace: Vec<String>,
    pub user: CurrentUser,
    #[serde(rename = "v")]
    pub version: u64,
}
<|endoftext|>


<commit_before>use std::{
    cmp::{min, Reverse},
    io,
    io::{Cursor, Write},
    ops::AddAssign,
};

use axum::{
    body,
    response::{IntoResponse, Response},
};
use bytes::{Buf, BufMut};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};
use shakmaty::{san::SanPlus, uci::Uci, ByColor, Chess, Color, Outcome};

use crate::{
    api::Limits,
    model::{GameId, GamePlayer, LaxDate, PreparedMove, PreparedResponse, RawUci, Stats},
    util::{sort_by_key_and_truncate, ByColorDef},
};

#[serde_as]
#[derive(Deserialize, Debug)]
pub struct MastersGameWithId {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    #[serde(flatten)]
    pub game: MastersGame,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct MastersGame {
    pub event: String,
    pub site: String,
    #[serde_as(as = "DisplayFromStr")]
    pub date: LaxDate,
    pub round: String,
    #[serde(flatten, with = "ByColorDef")]
    pub players: ByColor<GamePlayer>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, Uci>")]
    pub moves: Vec<Uci>,
}

impl MastersGame {
    fn outcome(&self) -> Outcome {
        Outcome::from_winner(self.winner)
    }

    fn write_pgn<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        writeln!(writer, "[Event \"{}\"]", self.event)?;
        writeln!(writer, "[Site \"{}\"]", self.site)?;
        writeln!(writer, "[Date \"{}\"]", self.date)?;
        writeln!(writer, "[Round \"{}\"]", self.round)?;
        writeln!(writer, "[White \"{}\"]", self.players.white.name)?;
        writeln!(writer, "[Black \"{}\"]", self.players.black.name)?;
        writeln!(writer, "[Result \"{}\"]", self.outcome())?;
        writeln!(writer, "[WhiteElo \"{}\"]", self.players.white.rating)?;
        writeln!(writer, "[BlackElo \"{}\"]", self.players.black.rating)?;
        writeln!(writer)?;

        let mut pos = Chess::default();

        for (i, uci) in self.moves.iter().enumerate() {
            let m = uci
                .to_move(&pos)
                .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
            if i % 2 == 0 {
                if i > 0 {
                    write!(writer, " ")?;
                }
                write!(writer, "{}.", i / 2 + 1)?;
            }
            let san = SanPlus::from_move_and_play_unchecked(&mut pos, &m);
            write!(writer, " {}", san)?;
        }

        if !self.moves.is_empty() {
            write!(writer, " ")?;
        }
        writeln!(writer, "{}", self.outcome())
    }
}

impl IntoResponse for MastersGame {
    fn into_response(self) -> Response {
        let mut buf = Cursor::new(Vec::new());
        self.write_pgn(&mut buf).expect("write pgn");

        Response::builder()
            .header(axum::http::header::CONTENT_TYPE, "application/x-chess-pgn")
            .body(body::boxed(body::Full::from(buf.into_inner())))
            .unwrap()
    }
}

#[derive(Debug, Default)]
pub struct MastersGroup {
    pub stats: Stats,
    pub games: Vec<(u16, GameId)>,
}

impl AddAssign for MastersGroup {
    fn add_assign(&mut self, rhs: MastersGroup) {
        self.stats += rhs.stats;
        self.games.extend(rhs.games);
    }
}

#[derive(Default, Debug)]
pub struct MastersEntry {
    pub groups: FxHashMap<RawUci, MastersGroup>,
}

impl MastersEntry {
    pub const SIZE_HINT: usize = 14;

    pub fn new_single(
        uci: Uci,
        id: GameId,
        outcome: Outcome,
        mover_rating: u16,
        opponent_rating: u16,
    ) -> MastersEntry {
        let mut groups = FxHashMap::with_capacity_and_hasher(1, Default::default());
        groups.insert(
            RawUci::from(uci),
            MastersGroup {
                stats: Stats::new_single(outcome, mover_rating),
                games: vec![(mover_rating.saturating_add(opponent_rating), id)],
            },
        );
        MastersEntry { groups }
    }

    pub fn extend_from_reader<B: Buf>(&mut self, buf: &mut B) {
        while buf.has_remaining() {
            let uci = RawUci::read(buf);
            let group = self.groups.entry(uci).or_default();
            group.stats += Stats::read(buf);
            let num_games = usize::from(buf.get_u8());
            group
                .games
                .extend((0..num_games).map(|_| (buf.get_u16_le(), GameId::read(buf))));
        }
    }

    pub fn write<B: BufMut>(&self, buf: &mut B) {
        let mut top_games = self
            .groups
            .values()
            .flat_map(|group| group.games.iter().copied())
            .collect();
        sort_by_key_and_truncate(&mut top_games, 15, |(sort_key, _)| Reverse(*sort_key));

        for (uci, group) in &self.groups {
            uci.write(buf);
            group.stats.write(buf);

            let num_games = if group.games.len() == 1 {
                1
            } else {
                group.games.iter().filter(|g| top_games.contains(g)).count()
            };
            buf.put_u8(num_games as u8);

            for (sort_key, id) in group
                .games
                .iter()
                .filter(|g| group.games.len() == 1 || top_games.contains(g))
            {
                buf.put_u16_le(*sort_key);
                id.write(buf);
            }
        }
    }

    fn total(&self) -> Stats {
        let mut sum = Stats::default();
        for group in self.groups.values() {
            sum += group.stats.clone();
        }
        sum
    }

    pub fn prepare(self, limits: &Limits) -> PreparedResponse {
        let total = self.total();

        let mut top_games = Vec::new();
        for (uci, group) in &self.groups {
            for (sort_key, game) in &group.games {
                top_games.push((*sort_key, uci.to_owned(), *game));
            }
        }
        sort_by_key_and_truncate(
            &mut top_games,
            min(limits.top_games, 15),
            |(sort_key, _, _)| Reverse(*sort_key),
        );

        let mut moves: Vec<PreparedMove> = self
            .groups
            .into_iter()
            .map(|(uci, group)| {
                let single_game = if group.stats.is_single() {
                    group.games.iter().map(|(_, id)| *id).next()
                } else {
                    None
                };
                PreparedMove {
                    uci: Uci::from(uci),
                    average_rating: group.stats.average_rating(),
                    average_opponent_rating: None,
                    game: single_game,
                    stats: group.stats,
                }
            })
            .collect();
        sort_by_key_and_truncate(&mut moves, limits.moves.unwrap_or(12), |m| {
            Reverse(m.stats.total())
        });

        PreparedResponse {
            total,
            moves,
            top_games: top_games
                .into_iter()
                .map(|(_, uci, game)| (Uci::from(uci), game))
                .collect(),
            recent_games: Vec::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use shakmaty::Square;

    use super::*;

    #[test]
    fn test_masters_entry() {
        let uci = Uci::Normal {
            from: Square::E2,
            to: Square::E4,
            promotion: None,
        };
        let game = "aaaaaaaa".parse().unwrap();
        let a = MastersEntry::new_single(uci.clone(), game, Outcome::Draw, 1600, 1700);

        let mut buf = Vec::with_capacity(MastersEntry::SIZE_HINT);
        a.write(&mut buf);
        assert_eq!(
            buf.len(),
            MastersEntry::SIZE_HINT,
            "optimized for single entries"
        );

        let mut reader = &buf[..];
        let mut deserialized = MastersEntry::default();
        deserialized.extend_from_reader(&mut reader);

        let group = deserialized.groups.get(&RawUci::from(uci)).unwrap();
        assert_eq!(group.stats.draws, 1);
        assert_eq!(group.games[0], (1600 + 1700, game));
    }
}
<commit_msg>add MAX_MASTERS_GAMES constant<commit_after>use std::{
    cmp::{min, Reverse},
    io,
    io::{Cursor, Write},
    ops::AddAssign,
};

use axum::{
    body,
    response::{IntoResponse, Response},
};
use bytes::{Buf, BufMut};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};
use shakmaty::{san::SanPlus, uci::Uci, ByColor, Chess, Color, Outcome};

use crate::{
    api::Limits,
    model::{GameId, GamePlayer, LaxDate, PreparedMove, PreparedResponse, RawUci, Stats},
    util::{sort_by_key_and_truncate, ByColorDef},
};

const MAX_MASTERS_GAMES: usize = 15;

#[serde_as]
#[derive(Deserialize, Debug)]
pub struct MastersGameWithId {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    #[serde(flatten)]
    pub game: MastersGame,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct MastersGame {
    pub event: String,
    pub site: String,
    #[serde_as(as = "DisplayFromStr")]
    pub date: LaxDate,
    pub round: String,
    #[serde(flatten, with = "ByColorDef")]
    pub players: ByColor<GamePlayer>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, Uci>")]
    pub moves: Vec<Uci>,
}

impl MastersGame {
    fn outcome(&self) -> Outcome {
        Outcome::from_winner(self.winner)
    }

    fn write_pgn<W: Write>(&self, writer: &mut W) -> io::Result<()> {
        writeln!(writer, "[Event \"{}\"]", self.event)?;
        writeln!(writer, "[Site \"{}\"]", self.site)?;
        writeln!(writer, "[Date \"{}\"]", self.date)?;
        writeln!(writer, "[Round \"{}\"]", self.round)?;
        writeln!(writer, "[White \"{}\"]", self.players.white.name)?;
        writeln!(writer, "[Black \"{}\"]", self.players.black.name)?;
        writeln!(writer, "[Result \"{}\"]", self.outcome())?;
        writeln!(writer, "[WhiteElo \"{}\"]", self.players.white.rating)?;
        writeln!(writer, "[BlackElo \"{}\"]", self.players.black.rating)?;
        writeln!(writer)?;

        let mut pos = Chess::default();

        for (i, uci) in self.moves.iter().enumerate() {
            let m = uci
                .to_move(&pos)
                .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
            if i % 2 == 0 {
                if i > 0 {
                    write!(writer, " ")?;
                }
                write!(writer, "{}.", i / 2 + 1)?;
            }
            let san = SanPlus::from_move_and_play_unchecked(&mut pos, &m);
            write!(writer, " {}", san)?;
        }

        if !self.moves.is_empty() {
            write!(writer, " ")?;
        }
        writeln!(writer, "{}", self.outcome())
    }
}

impl IntoResponse for MastersGame {
    fn into_response(self) -> Response {
        let mut buf = Cursor::new(Vec::new());
        self.write_pgn(&mut buf).expect("write pgn");

        Response::builder()
            .header(axum::http::header::CONTENT_TYPE, "application/x-chess-pgn")
            .body(body::boxed(body::Full::from(buf.into_inner())))
            .unwrap()
    }
}

#[derive(Debug, Default)]
pub struct MastersGroup {
    pub stats: Stats,
    pub games: Vec<(u16, GameId)>,
}

impl AddAssign for MastersGroup {
    fn add_assign(&mut self, rhs: MastersGroup) {
        self.stats += rhs.stats;
        self.games.extend(rhs.games);
    }
}

#[derive(Default, Debug)]
pub struct MastersEntry {
    pub groups: FxHashMap<RawUci, MastersGroup>,
}

impl MastersEntry {
    pub const SIZE_HINT: usize = 14;

    pub fn new_single(
        uci: Uci,
        id: GameId,
        outcome: Outcome,
        mover_rating: u16,
        opponent_rating: u16,
    ) -> MastersEntry {
        let mut groups = FxHashMap::with_capacity_and_hasher(1, Default::default());
        groups.insert(
            RawUci::from(uci),
            MastersGroup {
                stats: Stats::new_single(outcome, mover_rating),
                games: vec![(mover_rating.saturating_add(opponent_rating), id)],
            },
        );
        MastersEntry { groups }
    }

    pub fn extend_from_reader<B: Buf>(&mut self, buf: &mut B) {
        while buf.has_remaining() {
            let uci = RawUci::read(buf);
            let group = self.groups.entry(uci).or_default();
            group.stats += Stats::read(buf);
            let num_games = usize::from(buf.get_u8());
            group
                .games
                .extend((0..num_games).map(|_| (buf.get_u16_le(), GameId::read(buf))));
        }
    }

    pub fn write<B: BufMut>(&self, buf: &mut B) {
        let mut top_games = self
            .groups
            .values()
            .flat_map(|group| group.games.iter().copied())
            .collect();
        sort_by_key_and_truncate(&mut top_games, MAX_MASTERS_GAMES, |(sort_key, _)| Reverse(*sort_key));

        for (uci, group) in &self.groups {
            uci.write(buf);
            group.stats.write(buf);

            let num_games = if group.games.len() == 1 {
                1
            } else {
                group.games.iter().filter(|g| top_games.contains(g)).count()
            };
            buf.put_u8(num_games as u8);

            for (sort_key, id) in group
                .games
                .iter()
                .filter(|g| group.games.len() == 1 || top_games.contains(g))
            {
                buf.put_u16_le(*sort_key);
                id.write(buf);
            }
        }
    }

    fn total(&self) -> Stats {
        let mut sum = Stats::default();
        for group in self.groups.values() {
            sum += group.stats.clone();
        }
        sum
    }

    pub fn prepare(self, limits: &Limits) -> PreparedResponse {
        let total = self.total();

        let mut top_games = Vec::new();
        for (uci, group) in &self.groups {
            for (sort_key, game) in &group.games {
                top_games.push((*sort_key, uci.to_owned(), *game));
            }
        }
        sort_by_key_and_truncate(
            &mut top_games,
            min(limits.top_games, MAX_MASTERS_GAMES),
            |(sort_key, _, _)| Reverse(*sort_key),
        );

        let mut moves: Vec<PreparedMove> = self
            .groups
            .into_iter()
            .map(|(uci, group)| {
                let single_game = if group.stats.is_single() {
                    group.games.iter().map(|(_, id)| *id).next()
                } else {
                    None
                };
                PreparedMove {
                    uci: Uci::from(uci),
                    average_rating: group.stats.average_rating(),
                    average_opponent_rating: None,
                    game: single_game,
                    stats: group.stats,
                }
            })
            .collect();
        sort_by_key_and_truncate(&mut moves, limits.moves.unwrap_or(12), |m| {
            Reverse(m.stats.total())
        });

        PreparedResponse {
            total,
            moves,
            top_games: top_games
                .into_iter()
                .map(|(_, uci, game)| (Uci::from(uci), game))
                .collect(),
            recent_games: Vec::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use shakmaty::Square;

    use super::*;

    #[test]
    fn test_masters_entry() {
        let uci = Uci::Normal {
            from: Square::E2,
            to: Square::E4,
            promotion: None,
        };
        let game = "aaaaaaaa".parse().unwrap();
        let a = MastersEntry::new_single(uci.clone(), game, Outcome::Draw, 1600, 1700);

        let mut buf = Vec::with_capacity(MastersEntry::SIZE_HINT);
        a.write(&mut buf);
        assert_eq!(
            buf.len(),
            MastersEntry::SIZE_HINT,
            "optimized for single entries"
        );

        let mut reader = &buf[..];
        let mut deserialized = MastersEntry::default();
        deserialized.extend_from_reader(&mut reader);

        let group = deserialized.groups.get(&RawUci::from(uci)).unwrap();
        assert_eq!(group.stats.draws, 1);
        assert_eq!(group.games[0], (1600 + 1700, game));
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::rc::Rc;
use libc::c_char;

use sys::sdl as ll;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as isize,
    ReadError = ll::SDL_EFREAD as isize,
    WriteError = ll::SDL_EFWRITE as isize,
    SeekError = ll::SDL_EFSEEK as isize,
    UnsupportedError = ll::SDL_UNSUPPORTED as isize
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match *self {
            NoMemError => write!(f, "Out of memory"),
            ReadError => write!(f, "Error reading from datastream"),
            WriteError => write!(f, "Error writing to datastream"),
            SeekError => write!(f, "Error seeking in datastream"),
            UnsupportedError => write!(f, "Unknown SDL error")
        }
    }
}

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = ATOMIC_BOOL_INIT;

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
#[derive(Clone)]
pub struct Sdl {
    sdldrop: Rc<SdlDrop>
}

impl Sdl {
    #[inline]
    fn new() -> Result<Sdl, String> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
            } else {
                // Initialize SDL without any explicit subsystems (flags = 0).
                if ll::SDL_Init(0) == 0 {
                    Ok(Sdl {
                        sdldrop: Rc::new(SdlDrop)
                    })
                } else {
                    IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                    Err(get_error())
                }
            }
        }
    }

    /// Initializes the audio subsystem.
    #[inline]
    pub fn audio(&self) -> Result<AudioSubsystem, String> { AudioSubsystem::new(self) }

    /// Initializes the event subsystem.
    #[inline]
    pub fn event(&self) -> Result<EventSubsystem, String> { EventSubsystem::new(self) }

    /// Initializes the joystick subsystem.
    #[inline]
    pub fn joystick(&self) -> Result<JoystickSubsystem, String> { JoystickSubsystem::new(self) }

    /// Initializes the haptic subsystem.
    #[inline]
    pub fn haptic(&self) -> Result<HapticSubsystem, String> { HapticSubsystem::new(self) }

    /// Initializes the game controller subsystem.
    #[inline]
    pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> { GameControllerSubsystem::new(self) }

    /// Initializes the timer subsystem.
    #[inline]
    pub fn timer(&self) -> Result<TimerSubsystem, String> { TimerSubsystem::new(self) }

    /// Initializes the video subsystem.
    #[inline]
    pub fn video(&self) -> Result<VideoSubsystem, String> { VideoSubsystem::new(self) }

    /// Obtains the SDL event pump.
    ///
    /// At most one `EventPump` is allowed to be alive during the program's execution.
    /// If this function is called while an `EventPump` instance is alive, the function will return
    /// an error.
    #[inline]
    pub fn event_pump(&self) -> Result<EventPump, String> {
        EventPump::new(self)
    }

    #[inline]
    #[doc(hidden)]
    pub fn sdldrop(&self) -> Rc<SdlDrop> {
        self.sdldrop.clone()
    }
}

/// When SDL is no longer in use (the refcount in an `Rc<SdlDrop>` reaches 0), the library is quit.
#[doc(hidden)]
#[derive(Debug)]
pub struct SdlDrop;

impl Drop for SdlDrop {
    #[inline]
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe { ll::SDL_Quit(); }
    }
}

// No subsystem can implement `Send` because the destructor, `SDL_QuitSubSystem`,
// utilizes non-atomic reference counting and should thus be called on a single thread.
// Some subsystems have functions designed to be thread-safe, such as adding a timer or accessing
// the event queue. These subsystems implement `Sync`.

macro_rules! subsystem {
    ($name:ident, $flag:expr) => (
        impl $name {
            #[inline]
            fn new(sdl: &Sdl) -> Result<$name, String> {
                let result = unsafe { ll::SDL_InitSubSystem($flag) };

                if result == 0 {
                    Ok($name {
                        _subsystem_drop: Rc::new(SubsystemDrop {
                            _sdldrop: sdl.sdldrop.clone(),
                            flag: $flag
                        })
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    );
    ($name:ident, $flag:expr, nosync) => (
        #[derive(Debug, Clone)]
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
            }
        }

        subsystem!($name, $flag);
    );
    ($name:ident, $flag:expr, sync) => (
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>
        }
        unsafe impl Sync for $name {}

        impl $name {
            #[inline]
            pub fn clone(&self) -> $name {
                $name {
                    _subsystem_drop: self._subsystem_drop.clone()
                }
            }

            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
            }
        }

        subsystem!($name, $flag);
    )
}

/// When a subsystem is no longer in use (the refcount in an `Rc<SubsystemDrop>` reaches 0),
/// the subsystem is quit.
#[derive(Debug, Clone)]
struct SubsystemDrop {
    _sdldrop: Rc<SdlDrop>,
    flag: ll::SDL_InitFlag
}

impl Drop for SubsystemDrop {
    #[inline]
    fn drop(&mut self) {
        unsafe { ll::SDL_QuitSubSystem(self.flag); }
    }
}

subsystem!(AudioSubsystem, ll::SDL_INIT_AUDIO, nosync);
subsystem!(GameControllerSubsystem, ll::SDL_INIT_GAMECONTROLLER, nosync);
subsystem!(HapticSubsystem, ll::SDL_INIT_HAPTIC, nosync);
subsystem!(JoystickSubsystem, ll::SDL_INIT_JOYSTICK, nosync);
subsystem!(VideoSubsystem, ll::SDL_INIT_VIDEO, nosync);
// Timers can be added on other threads.
subsystem!(TimerSubsystem, ll::SDL_INIT_TIMER, sync);
// The event queue can be read from other threads.
subsystem!(EventSubsystem, ll::SDL_INIT_EVENTS, sync);

static mut IS_EVENT_PUMP_ALIVE: bool = false;

/// A thread-safe type that encapsulates SDL event-pumping functions.
pub struct EventPump {
    _sdldrop: Rc<SdlDrop>
}

impl EventPump {
    /// Obtains the SDL event pump.
    #[inline]
    fn new(sdl: &Sdl) -> Result<EventPump, String> {
        // Called on the main SDL thread.

        unsafe {
            if IS_EVENT_PUMP_ALIVE {
                Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
            } else {
                // Initialize the events subsystem, just in case none of the other subsystems have done it yet.
                let result = ll::SDL_InitSubSystem(ll::SDL_INIT_EVENTS);

                if result == 0 {
                    IS_EVENT_PUMP_ALIVE = true;

                    Ok(EventPump {
                        _sdldrop: sdl.sdldrop.clone()
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    }
}

impl Drop for EventPump {
    #[inline]
    fn drop(&mut self) {
        // Called on the main SDL thread.

        unsafe {
            assert!(IS_EVENT_PUMP_ALIVE);
            ll::SDL_QuitSubSystem(ll::SDL_INIT_EVENTS);
            IS_EVENT_PUMP_ALIVE = false;
        }
    }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let mut event_pump = sdl_context.event_pump().unwrap();
///
/// for event in event_pump.poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
#[inline]
pub fn init() -> Result<Sdl, String> { Sdl::new() }

pub fn get_error() -> String {
    unsafe {
        let err = ll::SDL_GetError();
        CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
    }
}

pub fn set_error(err: &str) -> Result<(), NulError> {
    let c_string = try!(CString::new(err));
    Ok(unsafe {
        ll::SDL_SetError(c_string.as_ptr() as *const c_char);
    })
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}
<commit_msg>Implement Error for SDL Error.<commit_after>use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::rc::Rc;
use libc::c_char;

use sys::sdl as ll;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = ll::SDL_ENOMEM as isize,
    ReadError = ll::SDL_EFREAD as isize,
    WriteError = ll::SDL_EFWRITE as isize,
    SeekError = ll::SDL_EFSEEK as isize,
    UnsupportedError = ll::SDL_UNSUPPORTED as isize
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match *self {
            NoMemError => write!(f, "Out of memory"),
            ReadError => write!(f, "Error reading from datastream"),
            WriteError => write!(f, "Error writing to datastream"),
            SeekError => write!(f, "Error seeking in datastream"),
            UnsupportedError => write!(f, "Unknown SDL error")
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        use self::Error::*;

        match *self {
            NoMemError => "out of memory",
            ReadError => "error reading from datastream",
            WriteError => "error writing to datastream",
            SeekError => "error seeking in datastream",
            UnsupportedError => "unknown SDL error"
        }
    }
}

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = ATOMIC_BOOL_INIT;

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
#[derive(Clone)]
pub struct Sdl {
    sdldrop: Rc<SdlDrop>
}

impl Sdl {
    #[inline]
    fn new() -> Result<Sdl, String> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
            } else {
                // Initialize SDL without any explicit subsystems (flags = 0).
                if ll::SDL_Init(0) == 0 {
                    Ok(Sdl {
                        sdldrop: Rc::new(SdlDrop)
                    })
                } else {
                    IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                    Err(get_error())
                }
            }
        }
    }

    /// Initializes the audio subsystem.
    #[inline]
    pub fn audio(&self) -> Result<AudioSubsystem, String> { AudioSubsystem::new(self) }

    /// Initializes the event subsystem.
    #[inline]
    pub fn event(&self) -> Result<EventSubsystem, String> { EventSubsystem::new(self) }

    /// Initializes the joystick subsystem.
    #[inline]
    pub fn joystick(&self) -> Result<JoystickSubsystem, String> { JoystickSubsystem::new(self) }

    /// Initializes the haptic subsystem.
    #[inline]
    pub fn haptic(&self) -> Result<HapticSubsystem, String> { HapticSubsystem::new(self) }

    /// Initializes the game controller subsystem.
    #[inline]
    pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> { GameControllerSubsystem::new(self) }

    /// Initializes the timer subsystem.
    #[inline]
    pub fn timer(&self) -> Result<TimerSubsystem, String> { TimerSubsystem::new(self) }

    /// Initializes the video subsystem.
    #[inline]
    pub fn video(&self) -> Result<VideoSubsystem, String> { VideoSubsystem::new(self) }

    /// Obtains the SDL event pump.
    ///
    /// At most one `EventPump` is allowed to be alive during the program's execution.
    /// If this function is called while an `EventPump` instance is alive, the function will return
    /// an error.
    #[inline]
    pub fn event_pump(&self) -> Result<EventPump, String> {
        EventPump::new(self)
    }

    #[inline]
    #[doc(hidden)]
    pub fn sdldrop(&self) -> Rc<SdlDrop> {
        self.sdldrop.clone()
    }
}

/// When SDL is no longer in use (the refcount in an `Rc<SdlDrop>` reaches 0), the library is quit.
#[doc(hidden)]
#[derive(Debug)]
pub struct SdlDrop;

impl Drop for SdlDrop {
    #[inline]
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe { ll::SDL_Quit(); }
    }
}

// No subsystem can implement `Send` because the destructor, `SDL_QuitSubSystem`,
// utilizes non-atomic reference counting and should thus be called on a single thread.
// Some subsystems have functions designed to be thread-safe, such as adding a timer or accessing
// the event queue. These subsystems implement `Sync`.

macro_rules! subsystem {
    ($name:ident, $flag:expr) => (
        impl $name {
            #[inline]
            fn new(sdl: &Sdl) -> Result<$name, String> {
                let result = unsafe { ll::SDL_InitSubSystem($flag) };

                if result == 0 {
                    Ok($name {
                        _subsystem_drop: Rc::new(SubsystemDrop {
                            _sdldrop: sdl.sdldrop.clone(),
                            flag: $flag
                        })
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    );
    ($name:ident, $flag:expr, nosync) => (
        #[derive(Debug, Clone)]
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
            }
        }

        subsystem!($name, $flag);
    );
    ($name:ident, $flag:expr, sync) => (
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>
        }
        unsafe impl Sync for $name {}

        impl $name {
            #[inline]
            pub fn clone(&self) -> $name {
                $name {
                    _subsystem_drop: self._subsystem_drop.clone()
                }
            }

            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl { sdldrop: self._subsystem_drop._sdldrop.clone() }
            }
        }

        subsystem!($name, $flag);
    )
}

/// When a subsystem is no longer in use (the refcount in an `Rc<SubsystemDrop>` reaches 0),
/// the subsystem is quit.
#[derive(Debug, Clone)]
struct SubsystemDrop {
    _sdldrop: Rc<SdlDrop>,
    flag: ll::SDL_InitFlag
}

impl Drop for SubsystemDrop {
    #[inline]
    fn drop(&mut self) {
        unsafe { ll::SDL_QuitSubSystem(self.flag); }
    }
}

subsystem!(AudioSubsystem, ll::SDL_INIT_AUDIO, nosync);
subsystem!(GameControllerSubsystem, ll::SDL_INIT_GAMECONTROLLER, nosync);
subsystem!(HapticSubsystem, ll::SDL_INIT_HAPTIC, nosync);
subsystem!(JoystickSubsystem, ll::SDL_INIT_JOYSTICK, nosync);
subsystem!(VideoSubsystem, ll::SDL_INIT_VIDEO, nosync);
// Timers can be added on other threads.
subsystem!(TimerSubsystem, ll::SDL_INIT_TIMER, sync);
// The event queue can be read from other threads.
subsystem!(EventSubsystem, ll::SDL_INIT_EVENTS, sync);

static mut IS_EVENT_PUMP_ALIVE: bool = false;

/// A thread-safe type that encapsulates SDL event-pumping functions.
pub struct EventPump {
    _sdldrop: Rc<SdlDrop>
}

impl EventPump {
    /// Obtains the SDL event pump.
    #[inline]
    fn new(sdl: &Sdl) -> Result<EventPump, String> {
        // Called on the main SDL thread.

        unsafe {
            if IS_EVENT_PUMP_ALIVE {
                Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
            } else {
                // Initialize the events subsystem, just in case none of the other subsystems have done it yet.
                let result = ll::SDL_InitSubSystem(ll::SDL_INIT_EVENTS);

                if result == 0 {
                    IS_EVENT_PUMP_ALIVE = true;

                    Ok(EventPump {
                        _sdldrop: sdl.sdldrop.clone()
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    }
}

impl Drop for EventPump {
    #[inline]
    fn drop(&mut self) {
        // Called on the main SDL thread.

        unsafe {
            assert!(IS_EVENT_PUMP_ALIVE);
            ll::SDL_QuitSubSystem(ll::SDL_INIT_EVENTS);
            IS_EVENT_PUMP_ALIVE = false;
        }
    }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let mut event_pump = sdl_context.event_pump().unwrap();
///
/// for event in event_pump.poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
#[inline]
pub fn init() -> Result<Sdl, String> { Sdl::new() }

pub fn get_error() -> String {
    unsafe {
        let err = ll::SDL_GetError();
        CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
    }
}

pub fn set_error(err: &str) -> Result<(), NulError> {
    let c_string = try!(CString::new(err));
    Ok(unsafe {
        ll::SDL_SetError(c_string.as_ptr() as *const c_char);
    })
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode); }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}
<|endoftext|>


<commit_before>use super::capi::*;
use super::def::Mpfr;

use std::cmp::Ordering;

impl PartialEq<Mpfr> for Mpfr {
    #[inline]
    fn eq(&self, other: &Mpfr) -> bool {
        if unsafe { mpfr_unordered_p(&self.mpfr, &other.mpfr) } == 0 {
            unsafe { mpfr_equal_p(&self.mpfr, &other.mpfr) != 0 }
        } else {
            false  // self or other is NaN and they cannot be compared
        }
    }
}

impl PartialOrd<Mpfr> for Mpfr {
    #[inline]
    fn partial_cmp(&self, other: &Mpfr) -> Option<Ordering> {
        if unsafe { mpfr_unordered_p(&self.mpfr, &other.mpfr) } == 0 {
            let cmp = unsafe { mpfr_cmp(&self.mpfr, &other.mpfr) };
            if cmp < 0 {
                Some(Ordering::Less)
            } else if cmp > 0 {
                Some(Ordering::Greater)
            } else {
                Some(Ordering::Equal)
            }
        } else {
            None  // self or other is NaN and they cannot be compared
        }
    }

    #[inline]
    fn lt(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_less_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn le(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_lessequal_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn gt(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_greater_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn ge(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_greaterequal_p(&self.mpfr, &other.mpfr) != 0 }
    }
}

#[cfg(test)]
mod test {
    use super::super::def::Mpfr;

    use float::{Float, RoundingMode};

    #[test]
    fn test_partial_eq() {
        assert!(mpfr!(0) == mpfr!(0));
        assert!(mpfr!(0) == Mpfr::zero(123));
        assert!(mpfr!(0) != mpfr!(1));
        assert!(mpfr_inf!() == mpfr_inf!());
        assert!(mpfr_neg_inf!() == mpfr_neg_inf!());
        assert!(mpfr_nan!() != mpfr_nan!());
    }

    #[test]
    fn test_partial_ord() {
        assert!(mpfr!(0) <= mpfr!(0));
        assert!(mpfr!(0) >= mpfr!(0));
        assert!(mpfr!(0) < mpfr!(1));
        assert!(mpfr!(1) > mpfr!(0));
        assert!(mpfr_neg_inf!() <= mpfr_neg_inf!());
        assert!(mpfr_inf!() >= mpfr_inf!());
        assert!(mpfr_neg_inf!() < mpfr_inf!());
        assert!(mpfr_inf!() > mpfr_neg_inf!());
        assert!(!(mpfr_nan!() <= mpfr_nan!()));
        assert!(!(mpfr_nan!() >= mpfr_nan!()));
        assert!(!(mpfr_nan!() < mpfr_nan!()));
        assert!(!(mpfr_nan!() > mpfr_nan!()));
    }
}
<commit_msg>Add test for partial_cmp.<commit_after>use super::capi::*;
use super::def::Mpfr;

use std::cmp::Ordering;

impl PartialEq<Mpfr> for Mpfr {
    #[inline]
    fn eq(&self, other: &Mpfr) -> bool {
        if unsafe { mpfr_unordered_p(&self.mpfr, &other.mpfr) } == 0 {
            unsafe { mpfr_equal_p(&self.mpfr, &other.mpfr) != 0 }
        } else {
            false  // self or other is NaN and they cannot be compared
        }
    }
}

impl PartialOrd<Mpfr> for Mpfr {
    #[inline]
    fn partial_cmp(&self, other: &Mpfr) -> Option<Ordering> {
        if unsafe { mpfr_unordered_p(&self.mpfr, &other.mpfr) } == 0 {
            let cmp = unsafe { mpfr_cmp(&self.mpfr, &other.mpfr) };
            if cmp < 0 {
                Some(Ordering::Less)
            } else if cmp > 0 {
                Some(Ordering::Greater)
            } else {
                Some(Ordering::Equal)
            }
        } else {
            None  // self or other is NaN and they cannot be compared
        }
    }

    #[inline]
    fn lt(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_less_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn le(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_lessequal_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn gt(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_greater_p(&self.mpfr, &other.mpfr) != 0 }
    }

    #[inline]
    fn ge(&self, other: &Mpfr) -> bool {
        unsafe { mpfr_greaterequal_p(&self.mpfr, &other.mpfr) != 0 }
    }
}

#[cfg(test)]
mod test {
    use super::super::def::Mpfr;

    use float::{Float, RoundingMode};

    use std::cmp::Ordering;

    #[test]
    fn test_partial_eq() {
        assert!(mpfr!(0) == mpfr!(0));
        assert!(mpfr!(0) == Mpfr::zero(123));
        assert!(mpfr!(0) != mpfr!(1));
        assert!(mpfr_inf!() == mpfr_inf!());
        assert!(mpfr_neg_inf!() == mpfr_neg_inf!());
        assert!(mpfr_nan!() != mpfr_nan!());
    }

    #[test]
    fn test_partial_ord_cmp() {
        assert_eq!(Ordering::Equal, mpfr!(0).partial_cmp(&mpfr!(0)).unwrap());
        assert_eq!(Ordering::Less, mpfr!(0).partial_cmp(&mpfr!(1)).unwrap());
        assert_eq!(Ordering::Greater, mpfr!(1).partial_cmp(&mpfr!(0)).unwrap());
        assert_eq!(Ordering::Equal, mpfr_neg_inf!().partial_cmp(&mpfr_neg_inf!()).unwrap());
        assert_eq!(Ordering::Equal, mpfr_inf!().partial_cmp(&mpfr_inf!()).unwrap());
        assert_eq!(Ordering::Less, mpfr_neg_inf!().partial_cmp(&mpfr_inf!()).unwrap());
        assert_eq!(Ordering::Greater, mpfr_inf!().partial_cmp(&mpfr_neg_inf!()).unwrap());
        assert!(mpfr_nan!().partial_cmp(&mpfr_nan!()).is_none());
    }

    #[test]
    fn test_partial_ord_rest() {
        assert!(mpfr!(0) <= mpfr!(0));
        assert!(mpfr!(0) >= mpfr!(0));
        assert!(mpfr!(0) < mpfr!(1));
        assert!(mpfr!(1) > mpfr!(0));
        assert!(mpfr_neg_inf!() <= mpfr_neg_inf!());
        assert!(mpfr_inf!() >= mpfr_inf!());
        assert!(mpfr_neg_inf!() < mpfr_inf!());
        assert!(mpfr_inf!() > mpfr_neg_inf!());
        assert!(!(mpfr_nan!() <= mpfr_nan!()));
        assert!(!(mpfr_nan!() >= mpfr_nan!()));
        assert!(!(mpfr_nan!() < mpfr_nan!()));
        assert!(!(mpfr_nan!() > mpfr_nan!()));
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]
extern crate extra;

use std::io::{self, Write};
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{basename} */ r#"
NAME
    basename - strip directory and suffix from filenames

SYNOPSIS
    basename [-z --zero] [-s | --suffix] SUFFIX [-a | --multiple] NAME...

DESCRIPTION
    Print NAME with any leading directory components removed. If a SUFFIX is provided, the suffix
    will be removed from NAME.

OPTIONS
    -a
    --multiple
        Support multiple arguments and treat each as a NAME

    -s
    --suffix=SUFFIX
        remove a trailing SUFFIX; implies -a

    -z
    --zero
        end each output line with NUL, not newline

    -h
    --help
        display this help and exit

EXAMPLE
    basename dir/filename.ext
        > filename.ext
    basename dir/filename.ext .ext
        > filename
    basename -a -s .ext one.ext two.ext three.ext
        > one two three

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

const HELP_INFO:       &'static str = "Try ‘basename --help’ for more information.\n";
const MISSING_OPERAND: &'static str = "missing operand\n";
const REQUIRES_OPTION: &'static str = "option requires an argument -- ‘s’\n";

fn main() {
    let stdout          = io::stdout();
    let mut stdout      = stdout.lock();
    let mut stderr      = io::stderr();
    let mut arguments   = std::env::args().skip(1);
    let mut multiple    = false;
    let mut zero        = false;
    let mut suffix      = String::new();
    let first_name: Option<String>;

    // Check input arguments for options.
    loop {
        if let Some(argument) = arguments.next() {
            // If the first character begins with a `-` then it is an option.
            if argument.chars().take(1).next().unwrap_or(' ') == '-' {
                match argument.as_str() {
                    "-a" | "--multiple" => multiple = true,
                    "-s" | "--suffix"   => {
                        if let Some(arg) = arguments.next() {
                            suffix = arg;
                            multiple = true;
                        } else {
                            stderr.write_all(REQUIRES_OPTION.as_bytes()).try(&mut stderr);
                            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                            stderr.flush().try(&mut stderr);
                            stdout.flush().try(&mut stderr);
                            std::process::exit(1);
                        }
                    },
                    "-z" | "--zero"     => zero = true,
                    "-h" | "--help"     => {
                        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
                        stdout.flush().try(&mut stderr);
                        std::process::exit(1);
                    },
                    _ => {
                        stderr.write_all("invalid option -- ‘".as_bytes()).try(&mut stderr);
                        stderr.write_all(argument.as_bytes()).try(&mut stderr);
                        stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                        stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                        stderr.flush().try(&mut stderr);
                        stdout.flush().try(&mut stderr);
                        std::process::exit(1);
                    }
                }
            } else {
                first_name = Some(argument); // Store the last argument so that it isn't lost.
                break
            }
        } else {
            stdout.write_all(MISSING_OPERAND.as_bytes()).try(&mut stderr);
            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
            std::process::exit(0);
        }
    }

    // Begin printing the basename of each following argument.
    if let Some(name) = first_name {
        if !multiple {
            // If multiple isn't set and there is more than one
            // argument, the second argument is the suffix.
            if let Some(potential_suffix) = arguments.next() {
                // Check if there is an extra operand and exit if true.
                if let Some(extra_operand) = arguments.next() {
                    stderr.write_all("extra operand ‘".as_bytes()).try(&mut stderr);
                    stderr.write_all(extra_operand.as_bytes()).try(&mut stderr);
                    stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                    stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    stdout.flush().try(&mut stderr);
                    std::process::exit(1);
                }

                // Otherwise, set the suffix variable to the argument we found.
                suffix = potential_suffix;
            }
        }
        basename(zero, &name, &suffix, &mut stdout, &mut stderr);
    }

    // If the multiple arguments flag was set, execute a loop to print each name.
    if multiple {
        for argument in arguments {
            basename(zero, &argument, &suffix, &mut stdout, &mut stderr);
        }
    }
}

/// Takes a file path as input and returns the basename of that path. If `zero` is set to true,
/// the name will be printed without a newline. If a suffix is set and the path contains that
/// suffix, the suffix will be removed.
fn basename(zero: bool, input: &str, suffix: &str, stdout: &mut io::StdoutLock, stderr: &mut io::Stderr) {
    // If the suffix variable is set, remove the suffix from the path string.
    let path = if suffix.is_empty() {
        input
    } else {
        let (prefix, input_suffix) = input.split_at(input.len() - suffix.len());
        if input_suffix == suffix { prefix } else { input }
    };

    // Only print the basename of the the path.
    let new_path = std::path::Path::new(&path);
    match new_path.file_name() {
        Some(base) => {
            if let Some(filename) = base.to_str() {
                stdout.write_all(filename.as_bytes()).try(stderr);
            } else {
                stderr.write_all("invalid unicode in path ‘".as_bytes()).try(stderr);
                stderr.write_all(path.as_bytes()).try(stderr);
                stderr.write_all("’\n".as_bytes()).try(stderr);
                stderr.flush().try(stderr);
                std::process::exit(1);
            }

        },
        None => {
            stderr.write_all("invalid path ‘".as_bytes()).try(stderr);
            stderr.write_all(path.as_bytes()).try(stderr);
            stderr.write_all("’\n".as_bytes()).try(stderr);
            stderr.flush().try(stderr);
            std::process::exit(1);
        }
    };

    // If zero is not enabled, print a newline.
    if !zero { stdout.write_all(b"\n").try(stderr); }
}
<commit_msg>Replace std::process::exit() Additional non-clippy-related fix to replace all instances std::process::exit() with process::exit().<commit_after>#![deny(warnings)]
extern crate extra;

use std::io::{self, Write};
use extra::option::OptionalExt;
use std::process;

const MAN_PAGE: &'static str = /* @MANSTART{basename} */ r#"
NAME
    basename - strip directory and suffix from filenames

SYNOPSIS
    basename [-z --zero] [-s | --suffix] SUFFIX [-a | --multiple] NAME...

DESCRIPTION
    Print NAME with any leading directory components removed. If a SUFFIX is provided, the suffix
    will be removed from NAME.

OPTIONS
    -a
    --multiple
        Support multiple arguments and treat each as a NAME

    -s
    --suffix=SUFFIX
        remove a trailing SUFFIX; implies -a

    -z
    --zero
        end each output line with NUL, not newline

    -h
    --help
        display this help and exit

EXAMPLE
    basename dir/filename.ext
        > filename.ext
    basename dir/filename.ext .ext
        > filename
    basename -a -s .ext one.ext two.ext three.ext
        > one two three

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

const HELP_INFO:       &'static str = "Try ‘basename --help’ for more information.\n";
const MISSING_OPERAND: &'static str = "missing operand\n";
const REQUIRES_OPTION: &'static str = "option requires an argument -- ‘s’\n";

fn main() {
    let stdout          = io::stdout();
    let mut stdout      = stdout.lock();
    let mut stderr      = io::stderr();
    let mut arguments   = std::env::args().skip(1);
    let mut multiple    = false;
    let mut zero        = false;
    let mut suffix      = String::new();
    let first_name: Option<String>;

    // Check input arguments for options.
    loop {
        if let Some(argument) = arguments.next() {
            // If the first character begins with a `-` then it is an option.
            if argument.chars().take(1).next().unwrap_or(' ') == '-' {
                match argument.as_str() {
                    "-a" | "--multiple" => multiple = true,
                    "-s" | "--suffix"   => {
                        if let Some(arg) = arguments.next() {
                            suffix = arg;
                            multiple = true;
                        } else {
                            stderr.write_all(REQUIRES_OPTION.as_bytes()).try(&mut stderr);
                            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                            stderr.flush().try(&mut stderr);
                            stdout.flush().try(&mut stderr);
                            process::exit(1);
                        }
                    },
                    "-z" | "--zero"     => zero = true,
                    "-h" | "--help"     => {
                        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
                        stdout.flush().try(&mut stderr);
                        process::exit(1);
                    },
                    _ => {
                        stderr.write_all("invalid option -- ‘".as_bytes()).try(&mut stderr);
                        stderr.write_all(argument.as_bytes()).try(&mut stderr);
                        stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                        stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                        stderr.flush().try(&mut stderr);
                        stdout.flush().try(&mut stderr);
                        process::exit(1);
                    }
                }
            } else {
                first_name = Some(argument); // Store the last argument so that it isn't lost.
                break
            }
        } else {
            stdout.write_all(MISSING_OPERAND.as_bytes()).try(&mut stderr);
            stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
            process::exit(0);
        }
    }

    // Begin printing the basename of each following argument.
    if let Some(name) = first_name {
        if !multiple {
            // If multiple isn't set and there is more than one
            // argument, the second argument is the suffix.
            if let Some(potential_suffix) = arguments.next() {
                // Check if there is an extra operand and exit if true.
                if let Some(extra_operand) = arguments.next() {
                    stderr.write_all("extra operand ‘".as_bytes()).try(&mut stderr);
                    stderr.write_all(extra_operand.as_bytes()).try(&mut stderr);
                    stderr.write_all("’\n".as_bytes()).try(&mut stderr);
                    stdout.write_all(HELP_INFO.as_bytes()).try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    stdout.flush().try(&mut stderr);
                    process::exit(1);
                }

                // Otherwise, set the suffix variable to the argument we found.
                suffix = potential_suffix;
            }
        }
        basename(zero, &name, &suffix, &mut stdout, &mut stderr);
    }

    // If the multiple arguments flag was set, execute a loop to print each name.
    if multiple {
        for argument in arguments {
            basename(zero, &argument, &suffix, &mut stdout, &mut stderr);
        }
    }
}

/// Takes a file path as input and returns the basename of that path. If `zero` is set to true,
/// the name will be printed without a newline. If a suffix is set and the path contains that
/// suffix, the suffix will be removed.
fn basename(zero: bool, input: &str, suffix: &str, stdout: &mut io::StdoutLock, stderr: &mut io::Stderr) {
    // If the suffix variable is set, remove the suffix from the path string.
    let path = if suffix.is_empty() {
        input
    } else {
        let (prefix, input_suffix) = input.split_at(input.len() - suffix.len());
        if input_suffix == suffix { prefix } else { input }
    };

    // Only print the basename of the the path.
    let new_path = std::path::Path::new(&path);
    match new_path.file_name() {
        Some(base) => {
            if let Some(filename) = base.to_str() {
                stdout.write_all(filename.as_bytes()).try(stderr);
            } else {
                stderr.write_all("invalid unicode in path ‘".as_bytes()).try(stderr);
                stderr.write_all(path.as_bytes()).try(stderr);
                stderr.write_all("’\n".as_bytes()).try(stderr);
                stderr.flush().try(stderr);
                process::exit(1);
            }

        },
        None => {
            stderr.write_all("invalid path ‘".as_bytes()).try(stderr);
            stderr.write_all(path.as_bytes()).try(stderr);
            stderr.write_all("’\n".as_bytes()).try(stderr);
            stderr.flush().try(stderr);
            process::exit(1);
        }
    };

    // If zero is not enabled, print a newline.
    if !zero { stdout.write_all(b"\n").try(stderr); }
}
<|endoftext|>


<commit_before>//! # Builders Sample
//!
//! This sample demonstrates how to create a widget using the builders.
//! These allow to set construct-only properties and other construct
//! properties when creating the widget.

extern crate gio;
extern crate gtk;

use gio::prelude::*;
use gtk::prelude::*;

use std::env::args;

fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindowBuilder::new()
        .application(application)
        .title("First GTK+ Program")
        .border_width(10)
        .window_position(gtk::WindowPosition::Center)
        .default_width(350)
        .default_height(70)
        .build();

    let button = gtk::LockButtonBuilder::new()
        .text_lock("Lock")
        .text_unlock("Unlock")
        .build();

    window.add(&button);

    window.show_all();
}

fn main() {
    let application = gtk::Application::new(Some("com.github.gtk-rs.examples.basic"),
                                            Default::default())
                                       .expect("Initialization failed...");

    application.connect_activate(|app| {
        build_ui(app);
    });

    application.run(&args().collect::<Vec<_>>());
}
<commit_msg>Cargo fmt<commit_after>//! # Builders Sample
//!
//! This sample demonstrates how to create a widget using the builders.
//! These allow to set construct-only properties and other construct
//! properties when creating the widget.

extern crate gio;
extern crate gtk;

use gio::prelude::*;
use gtk::prelude::*;

use std::env::args;

fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindowBuilder::new()
        .application(application)
        .title("First GTK+ Program")
        .border_width(10)
        .window_position(gtk::WindowPosition::Center)
        .default_width(350)
        .default_height(70)
        .build();

    let button = gtk::LockButtonBuilder::new()
        .text_lock("Lock")
        .text_unlock("Unlock")
        .build();

    window.add(&button);

    window.show_all();
}

fn main() {
    let application =
        gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default())
            .expect("Initialization failed...");

    application.connect_activate(|app| {
        build_ui(app);
    });

    application.run(&args().collect::<Vec<_>>());
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ffi::CString;
use std::fs;
use std::io;
use std::os::unix::prelude::*;
use std::path::Path;

use self::libc::{c_int, c_char, timeval, time_t, suseconds_t};
use self::libc::{timespec};

use FileTime;

cfg_if! {
    if #[cfg(target_os = "linux")] {
        mod linux;
        pub use self::linux::*;
    // netbsd, openbsd and freebsd should use utimensat, but the call is not
    // in the latest rust libc (0.2.43). as soon as a new version is available
    // these target_os'es should be added back in.
    } else if #[cfg(any(target_os = "android",
                        target_os = "solaris",
                        target_os = "emscripten"))] {
        mod utimensat;
        pub use self::utimensat::*;
    } else {
        mod utimes;
        pub use self::utimes::*;
    }
}

#[allow(dead_code)]
fn utimes(p: &Path,
          atime: FileTime,
          mtime: FileTime,
          utimes: unsafe extern fn(*const c_char, *const timeval) -> c_int)
    -> io::Result<()>
{
    let times = [to_timeval(&atime), to_timeval(&mtime)];
    let p = try!(CString::new(p.as_os_str().as_bytes()));
    return if unsafe { utimes(p.as_ptr() as *const _, times.as_ptr()) == 0 } {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    };

    fn to_timeval(ft: &FileTime) -> timeval {
        timeval {
            tv_sec: ft.seconds() as time_t,
            tv_usec: (ft.nanoseconds() / 1000) as suseconds_t,
        }
    }
}

#[allow(dead_code)]
fn utimensat(p: &Path,
             atime: FileTime,
             mtime: FileTime,
             f: unsafe extern fn(c_int, *const c_char, *const timespec, c_int) -> c_int,
             flags: c_int)
    -> io::Result<()>
{
    let times = [to_timespec(&atime), to_timespec(&mtime)];
    let p = try!(CString::new(p.as_os_str().as_bytes()));
    let rc = unsafe {
        f(libc::AT_FDCWD, p.as_ptr() as *const _, times.as_ptr(), flags)
    };
    return if rc == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    };

    fn to_timespec(ft: &FileTime) -> timespec {
        timespec {
            tv_sec: ft.seconds() as time_t,
            tv_nsec: ft.nanoseconds() as _,
        }
    }
}

pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.mtime(),
        nanos: meta.mtime_nsec() as u32,
    }
}

pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.atime(),
        nanos: meta.atime_nsec() as u32,
    }
}

pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> {
    macro_rules! birthtim {
        ($(($e:expr, $i:ident)),*) => {
            #[cfg(any($(target_os = $e),*))]
            fn imp(meta: &fs::Metadata) -> Option<FileTime> {
                $(
                    #[cfg(target_os = $e)]
                    use std::os::$i::fs::MetadataExt;
                )*
                Some(FileTime {
                    seconds: meta.st_birthtime(),
                    nanos: meta.st_birthtime_nsec() as u32,
                })
            }

            #[cfg(all($(not(target_os = $e)),*))]
            fn imp(_meta: &fs::Metadata) -> Option<FileTime> {
                None
            }
        }
    }

    birthtim! {
        ("bitrig", bitrig),
        ("freebsd", freebsd),
        ("ios", ios),
        ("macos", macos),
        ("openbsd", openbsd)
    }

    imp(meta)
}
<commit_msg>OpenBSD doesn't have lutimes so use utimensat instead.<commit_after>extern crate libc;

use std::ffi::CString;
use std::fs;
use std::io;
use std::os::unix::prelude::*;
use std::path::Path;

use self::libc::{c_int, c_char, timeval, time_t, suseconds_t};
use self::libc::{timespec};

use FileTime;

cfg_if! {
    if #[cfg(target_os = "linux")] {
        mod linux;
        pub use self::linux::*;
    // netbsd, openbsd and freebsd should use utimensat, but the call is not
    // in the latest rust libc (0.2.43). as soon as a new version is available
    // these target_os'es should be added back in.
    } else if #[cfg(any(target_os = "android",
                        target_os = "solaris",
                        target_os = "emscripten",
                        target_os = "openbsd"))] {
        mod utimensat;
        pub use self::utimensat::*;
    } else {
        mod utimes;
        pub use self::utimes::*;
    }
}

#[allow(dead_code)]
fn utimes(p: &Path,
          atime: FileTime,
          mtime: FileTime,
          utimes: unsafe extern fn(*const c_char, *const timeval) -> c_int)
    -> io::Result<()>
{
    let times = [to_timeval(&atime), to_timeval(&mtime)];
    let p = try!(CString::new(p.as_os_str().as_bytes()));
    return if unsafe { utimes(p.as_ptr() as *const _, times.as_ptr()) == 0 } {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    };

    fn to_timeval(ft: &FileTime) -> timeval {
        timeval {
            tv_sec: ft.seconds() as time_t,
            tv_usec: (ft.nanoseconds() / 1000) as suseconds_t,
        }
    }
}

#[allow(dead_code)]
fn utimensat(p: &Path,
             atime: FileTime,
             mtime: FileTime,
             f: unsafe extern fn(c_int, *const c_char, *const timespec, c_int) -> c_int,
             flags: c_int)
    -> io::Result<()>
{
    let times = [to_timespec(&atime), to_timespec(&mtime)];
    let p = try!(CString::new(p.as_os_str().as_bytes()));
    let rc = unsafe {
        f(libc::AT_FDCWD, p.as_ptr() as *const _, times.as_ptr(), flags)
    };
    return if rc == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    };

    fn to_timespec(ft: &FileTime) -> timespec {
        timespec {
            tv_sec: ft.seconds() as time_t,
            tv_nsec: ft.nanoseconds() as _,
        }
    }
}

pub fn from_last_modification_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.mtime(),
        nanos: meta.mtime_nsec() as u32,
    }
}

pub fn from_last_access_time(meta: &fs::Metadata) -> FileTime {
    FileTime {
        seconds: meta.atime(),
        nanos: meta.atime_nsec() as u32,
    }
}

pub fn from_creation_time(meta: &fs::Metadata) -> Option<FileTime> {
    macro_rules! birthtim {
        ($(($e:expr, $i:ident)),*) => {
            #[cfg(any($(target_os = $e),*))]
            fn imp(meta: &fs::Metadata) -> Option<FileTime> {
                $(
                    #[cfg(target_os = $e)]
                    use std::os::$i::fs::MetadataExt;
                )*
                Some(FileTime {
                    seconds: meta.st_birthtime(),
                    nanos: meta.st_birthtime_nsec() as u32,
                })
            }

            #[cfg(all($(not(target_os = $e)),*))]
            fn imp(_meta: &fs::Metadata) -> Option<FileTime> {
                None
            }
        }
    }

    birthtim! {
        ("bitrig", bitrig),
        ("freebsd", freebsd),
        ("ios", ios),
        ("macos", macos),
        ("openbsd", openbsd)
    }

    imp(meta)
}
<|endoftext|>