<commit_before>pub mod responses {

    use std::fmt;

    use serde::Deserialize;
    use serde_json as json;

    #[derive(Deserialize, Debug)]
    pub struct AuthResponse {
        pub session: SessionResponse,
    }

    /// Response to an Authentication request.
    ///
    /// Contains a Session Key and the username of the authenticated Last.fm user and a subscriber ID.
    /// Only the Session Key is used internally by the crate; the other values are exposed as they may have some value
    /// for clients.
    ///
    /// [Authentication API Requests Documentation](https://www.last.fm/api/authspec)
    #[derive(Deserialize, Debug, Clone)]
    pub struct SessionResponse {
        pub key: String,
        pub subscriber: i64,
        pub name: String,
    }

    #[derive(Deserialize)]
    pub struct NowPlayingResponseWrapper {
        pub nowplaying: NowPlayingResponse,
    }

    /// Response to a Now Playing request.
    ///
    /// Represents a response to a Now Playing API request. This type can often be ignored by clients. All of the
    /// fields are [`CorrectableString`] types, which can be used to see if Last.fm applied any metadata correction
    /// to your artist, song or album.
    ///
    /// [Now Playing Request API Documentation](https://www.last.fm/api/show/track.updateNowPlaying)
    #[derive(Deserialize, Debug)]
    pub struct NowPlayingResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
    }

    #[derive(Deserialize)]
    pub struct ScrobbleResponseWrapper {
        pub scrobbles: SingleScrobble,
    }

    #[derive(Deserialize)]
    pub struct SingleScrobble {
        pub scrobble: ScrobbleResponse,
    }

    /// Response to a Scrobble request
    ///
    /// Represents a response to a Scrobble API request. Contains the results of the Scrobble call, including any
    /// metadata corrections the Last.fm API made to the arist/track/album submitted.
    ///
    /// [Scrobble Request API Documentation](https://www.last.fm/api/show/track.scrobble)
    #[derive(Deserialize, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleList"]
    #[CollectionDerives = "Debug, Deserialize"]
    pub struct ScrobbleResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
        pub timestamp: String,
    }

    /// Response to a Batch Scrobble request
    ///
    /// Represents a response to a batched Scrobble request. Contains the results of the Scrobble call, including
    /// any metadata corrections the Last.fm API made to the arist/track/album submitted.
    ///
    /// [Scrobble Request API Documentation](https://www.last.fm/api/show/track.scrobble)
    #[derive(Debug)]
    pub struct BatchScrobbleResponse {
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbleResponseWrapper {
        pub scrobbles: BatchScrobbles,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbles {
        #[serde(rename = "scrobble")]
        pub scrobbles: ScrobbleList,
    }

    /// Represents a string that can be marked as 'corrected' by the Last.fm API.
    ///
    /// All Scrobble/NowPlaying responses have their fields as `CorrectableString`'s. The API will sometimes change
    /// the artist/song name/album name data that you have submitted. For example - it is common for Bjork to be turned
    /// into Björk by the API; the modified artist field would be marked `corrected = true`, `text = "Björk".
    ///
    /// Most clients can ignore these corrections, but the information is exposed for clients that require it.
    ///
    /// [Meta-Data Correction Documentation](https://www.last.fm/api/scrobbling#meta-data-corrections)
    #[derive(Deserialize, Debug)]
    pub struct CorrectableString {
        #[serde(deserialize_with = "CorrectableString::deserialize_corrected_field")]
        pub corrected: bool,
        #[serde(rename = "#text", default)]
        pub text: String,
    }

    impl CorrectableString {
        fn deserialize_corrected_field<'de, D>(de: D) -> Result<bool, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let deser_result: json::Value = serde::Deserialize::deserialize(de)?;
            match deser_result {
                json::Value::String(ref s) if &*s == "1" => Ok(true),
                json::Value::String(ref s) if &*s == "0" => Ok(false),
                _ => Err(serde::de::Error::custom("Unexpected value")),
            }
        }
    }

    impl fmt::Display for CorrectableString {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", self.text)
        }
    }
}

pub mod metadata {

    use std::collections::HashMap;

    /// Repesents a single music track played at a point in time. In the Last.fm universe, this is known as a
    /// "scrobble".
    ///
    /// Takes an artist, track and album name. Can hold a timestamp indicating when the track was listened to.
    /// `Scrobble` objects are submitted via [`Scrobbler::now_playing`], [`Scrobbler::scrobble`] and batches of
    /// Scrobbles are sent via [`Scrobbler::scrobble_batch`].
    ///
    /// [`Scrobbler::now_playing`]: struct.Scrobbler.html#method.now_playing
    /// [`Scrobbler::scrobble`]: struct.Scrobbler.html#method.scrobble
    /// [`Scrobbler::scrobble_batch`]: struct.Scrobbler.html#method.scrobble_batch
    #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleBatch"]
    #[CollectionDoc = "A batch of Scrobbles to be submitted to Last.fm together."]
    #[CollectionDerives = "Clone, Debug"]
    pub struct Scrobble {
        artist: String,
        track: String,
        album: String,

        timestamp: Option<u64>,
    }

    impl Scrobble {

        /// Constructs a new Scrobble instance, representing a single playthrough of a music track. `Scrobble`s are
        /// submitted to Last.fm via an instance of [`Scrobbler`]. A new `Scrobble` requires an artist name, song/track
        /// name, and an album name.
        ///
        /// # Example
        /// ```ignore
        /// let scrobble = Scrobble::new("Example Artist", "Example Track", "Example Album")
        /// ```
        ///
        /// [`Scrobbler`]: struct.Scrobbler.html
        pub fn new(artist: &str, track: &str, album: &str) -> Self {
            Self {
                artist: artist.to_owned(),
                track: track.to_owned(),
                album: album.to_owned(),
                timestamp: None,
            }
        }

        /// Sets the timestamp (date/time of play) of a Scrobble. Used in a builder-style pattern, typically after
        /// [`Scrobble::new`].
        ///
        /// # Example
        /// ```ignore
        /// let mut scrobble = Scrobble::new(...).with_timestamp(12345);
        /// ```
        ///
        /// # Note on Timestamps
        /// Scrobbles without timestamps are automatically assigned a timestamp of the current time when
        /// submitted via [`Scrobbler::scrobble`] or [`Scrobbler::scrobble_batch`]. Timestamps only need to be
        /// explicitly set when you are submitting a Scrobble at a point in the past, or in the future.
        ///
        /// [`Scrobble::new`]: struct.Scrobble.html#method.new
        /// [`Scrobbler::scrobble`]: struct.Scrobbler.html#method.scrobble
        /// [`Scrobbler::scrobble_batch`]: struct.Scrobbler.html#method.scrobble_batch
        pub fn with_timestamp(&mut self, timestamp: u64) -> &mut Self {
            self.timestamp = Some(timestamp);
            self
        }

        /// Converts the Scrobble metadata (track name, artist & album name) into a `HashMap`. Map keys are
        /// `"track"`, `"artist"` and `"album"`. If a timestamp is set, it will be present in the map under key
        /// `"timestamp"`.
        ///
        /// # Example
        /// ```ignore
        /// let scrobble = Scrobble::new("Example Artist", ...);
        /// let scrobble_map = scrobble.as_map();
        /// assert_eq!(scrobble_map.get("artist"), "Example Artist");
        /// ```
        pub fn as_map(&self) -> HashMap<String, String> {
            let mut params = HashMap::new();
            params.insert("track".to_string(), self.track.clone());
            params.insert("artist".to_string(), self.artist.clone());
            params.insert("album".to_string(), self.album.clone());

            if let Some(timestamp) = self.timestamp {
                params.insert("timestamp".to_string(), timestamp.to_string());
            }

            params
        }

        /// Returns the `Scrobble`'s artist name
        pub fn artist(&self) -> &str {
            &self.artist
        }

        /// Returns the `Scrobble`'s track name
        pub fn track(&self) -> &str {
            &self.track
        }

        /// Returns the `Scrobble`'s album name
        pub fn album(&self) -> &str {
            &self.album
        }

    }

    /// Converts from tuple of `&str`s in the form `(artist, track, album)`
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<&(&str, &str, &str)> for Scrobble {
        fn from((artist, track, album): &(&str, &str, &str)) -> Self {
            Scrobble::new(artist, track, album)
        }
    }

    /// Converts from tuple of `String`s in the form `(artist, track, album)`
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<&(String, String, String)> for Scrobble {
        fn from((artist, track, album): &(String, String, String)) -> Self {
            Scrobble::new(artist, track, album)
        }
    }

    /// Converts from vector of `&str` tuples, in the form `(artist, track, album)`.
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<Vec<(&str, &str, &str)>> for ScrobbleBatch {
        fn from(collection: Vec<(&str, &str, &str)>) -> Self {
            let scrobbles: Vec<Scrobble> = collection
                                .iter()
                                .map(Scrobble::from)
                                .collect();

            ScrobbleBatch::from(scrobbles)
        }
    }

    /// Converts from vector of `String` tuples, in the form `(artist, track, album)`.
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<Vec<(String, String, String)>> for ScrobbleBatch {
        fn from(collection: Vec<(String, String, String)>) -> Self {
            let scrobbles: Vec<Scrobble> = collection
                                .iter()
                                .map(Scrobble::from)
                                .collect();

            ScrobbleBatch::from(scrobbles)
        }
    }

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

        #[test]
        fn make_scrobble() {
            let mut scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );
            scrobble.with_timestamp(1337);
            assert_eq!(scrobble.artist(), "foo floyd and the fruit flies");
            assert_eq!(scrobble.track(), "old bananas");
            assert_eq!(scrobble.album(), "old bananas");
            assert_eq!(scrobble.timestamp, Some(1337));
        }

        #[test]
        fn make_scrobble_check_map() {
            let scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );

            let params = scrobble.as_map();
            assert_eq!(params["artist"], "foo floyd and the fruit flies");
            assert_eq!(params["track"], "old bananas");
            assert_eq!(params["album"], "old bananas");
        }
    }
}
<commit_msg>Fix response parsing where a ScrobbleBatch only has a single Scrobble (fixes #45)<commit_after>pub mod responses {

    use std::fmt;

    use serde::Deserialize;
    use serde::de::{Deserializer, Visitor, MapAccess, SeqAccess};
    use serde_json as json;

    #[derive(Deserialize, Debug)]
    pub struct AuthResponse {
        pub session: SessionResponse,
    }

    /// Response to an Authentication request.
    ///
    /// Contains a Session Key and the username of the authenticated Last.fm user and a subscriber ID.
    /// Only the Session Key is used internally by the crate; the other values are exposed as they may have some value
    /// for clients.
    ///
    /// [Authentication API Requests Documentation](https://www.last.fm/api/authspec)
    #[derive(Deserialize, Debug, Clone)]
    pub struct SessionResponse {
        pub key: String,
        pub subscriber: i64,
        pub name: String,
    }

    #[derive(Deserialize)]
    pub struct NowPlayingResponseWrapper {
        pub nowplaying: NowPlayingResponse,
    }

    /// Response to a Now Playing request.
    ///
    /// Represents a response to a Now Playing API request. This type can often be ignored by clients. All of the
    /// fields are [`CorrectableString`] types, which can be used to see if Last.fm applied any metadata correction
    /// to your artist, song or album.
    ///
    /// [Now Playing Request API Documentation](https://www.last.fm/api/show/track.updateNowPlaying)
    #[derive(Deserialize, Debug)]
    pub struct NowPlayingResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
    }

    #[derive(Deserialize)]
    pub struct ScrobbleResponseWrapper {
        pub scrobbles: SingleScrobble,
    }

    #[derive(Deserialize)]
    pub struct SingleScrobble {
        pub scrobble: ScrobbleResponse,
    }

    /// Response to a Scrobble request
    ///
    /// Represents a response to a Scrobble API request. Contains the results of the Scrobble call, including any
    /// metadata corrections the Last.fm API made to the arist/track/album submitted.
    ///
    /// [Scrobble Request API Documentation](https://www.last.fm/api/show/track.scrobble)
    #[derive(Deserialize, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleList"]
    #[CollectionDerives = "Debug, Deserialize"]
    pub struct ScrobbleResponse {
        pub artist: CorrectableString,
        pub album: CorrectableString,
        #[serde(rename = "albumArtist")]
        pub album_artist: CorrectableString,
        pub track: CorrectableString,
        pub timestamp: String,
    }

    /// Response to a Batch Scrobble request
    ///
    /// Represents a response to a batched Scrobble request. Contains the results of the Scrobble call, including
    /// any metadata corrections the Last.fm API made to the arist/track/album submitted.
    ///
    /// [Scrobble Request API Documentation](https://www.last.fm/api/show/track.scrobble)
    #[derive(Debug)]
    pub struct BatchScrobbleResponse {
        pub scrobbles: ScrobbleList,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbleResponseWrapper {
        pub scrobbles: BatchScrobbles,
    }

    #[derive(Deserialize, Debug)]
    pub struct BatchScrobbles {
        #[serde(deserialize_with = "BatchScrobbles::deserialize_response_scrobbles")]
        #[serde(rename = "scrobble")]
        pub scrobbles: ScrobbleList,
    }

    impl BatchScrobbles {
        fn deserialize_response_scrobbles<'de, D>(de: D) -> Result<ScrobbleList, D::Error>
        where
            D: Deserializer<'de>,
        {

            let deser_result: json::Value = serde::Deserialize::deserialize(de)?;
            let scrobbles = match deser_result {
                obj@json::Value::Object(_) => {
                    let scrobble: ScrobbleResponse = serde_json::from_value(obj).expect("Parsing scrobble failed");
                    ScrobbleList::from(vec!(scrobble))
                }
                arr@json::Value::Array(_) => {
                    let scrobbles: ScrobbleList = serde_json::from_value(arr).expect("Parsing scrobble list failed");
                    scrobbles
                },
                _ => ScrobbleList::from(vec!())
            };
            Ok(scrobbles)
        }
    }

    /// Represents a string that can be marked as 'corrected' by the Last.fm API.
    ///
    /// All Scrobble/NowPlaying responses have their fields as `CorrectableString`'s. The API will sometimes change
    /// the artist/song name/album name data that you have submitted. For example - it is common for Bjork to be turned
    /// into Björk by the API; the modified artist field would be marked `corrected = true`, `text = "Björk".
    ///
    /// Most clients can ignore these corrections, but the information is exposed for clients that require it.
    ///
    /// [Meta-Data Correction Documentation](https://www.last.fm/api/scrobbling#meta-data-corrections)
    #[derive(Deserialize, Debug)]
    pub struct CorrectableString {
        #[serde(deserialize_with = "CorrectableString::deserialize_corrected_field")]
        pub corrected: bool,
        #[serde(rename = "#text", default)]
        pub text: String,
    }

    impl CorrectableString {
        fn deserialize_corrected_field<'de, D>(de: D) -> Result<bool, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let deser_result: json::Value = serde::Deserialize::deserialize(de)?;
            match deser_result {
                json::Value::String(ref s) if &*s == "1" => Ok(true),
                json::Value::String(ref s) if &*s == "0" => Ok(false),
                _ => Err(serde::de::Error::custom("Unexpected value")),
            }
        }
    }

    impl fmt::Display for CorrectableString {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", self.text)
        }
    }
}

pub mod metadata {

    use std::collections::HashMap;

    /// Repesents a single music track played at a point in time. In the Last.fm universe, this is known as a
    /// "scrobble".
    ///
    /// Takes an artist, track and album name. Can hold a timestamp indicating when the track was listened to.
    /// `Scrobble` objects are submitted via [`Scrobbler::now_playing`], [`Scrobbler::scrobble`] and batches of
    /// Scrobbles are sent via [`Scrobbler::scrobble_batch`].
    ///
    /// [`Scrobbler::now_playing`]: struct.Scrobbler.html#method.now_playing
    /// [`Scrobbler::scrobble`]: struct.Scrobbler.html#method.scrobble
    /// [`Scrobbler::scrobble_batch`]: struct.Scrobbler.html#method.scrobble_batch
    #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, WrappedVec)]
    #[CollectionName = "ScrobbleBatch"]
    #[CollectionDoc = "A batch of Scrobbles to be submitted to Last.fm together."]
    #[CollectionDerives = "Clone, Debug"]
    pub struct Scrobble {
        artist: String,
        track: String,
        album: String,

        timestamp: Option<u64>,
    }

    impl Scrobble {

        /// Constructs a new Scrobble instance, representing a single playthrough of a music track. `Scrobble`s are
        /// submitted to Last.fm via an instance of [`Scrobbler`]. A new `Scrobble` requires an artist name, song/track
        /// name, and an album name.
        ///
        /// # Example
        /// ```ignore
        /// let scrobble = Scrobble::new("Example Artist", "Example Track", "Example Album")
        /// ```
        ///
        /// [`Scrobbler`]: struct.Scrobbler.html
        pub fn new(artist: &str, track: &str, album: &str) -> Self {
            Self {
                artist: artist.to_owned(),
                track: track.to_owned(),
                album: album.to_owned(),
                timestamp: None,
            }
        }

        /// Sets the timestamp (date/time of play) of a Scrobble. Used in a builder-style pattern, typically after
        /// [`Scrobble::new`].
        ///
        /// # Example
        /// ```ignore
        /// let mut scrobble = Scrobble::new(...).with_timestamp(12345);
        /// ```
        ///
        /// # Note on Timestamps
        /// Scrobbles without timestamps are automatically assigned a timestamp of the current time when
        /// submitted via [`Scrobbler::scrobble`] or [`Scrobbler::scrobble_batch`]. Timestamps only need to be
        /// explicitly set when you are submitting a Scrobble at a point in the past, or in the future.
        ///
        /// [`Scrobble::new`]: struct.Scrobble.html#method.new
        /// [`Scrobbler::scrobble`]: struct.Scrobbler.html#method.scrobble
        /// [`Scrobbler::scrobble_batch`]: struct.Scrobbler.html#method.scrobble_batch
        pub fn with_timestamp(&mut self, timestamp: u64) -> &mut Self {
            self.timestamp = Some(timestamp);
            self
        }

        /// Converts the Scrobble metadata (track name, artist & album name) into a `HashMap`. Map keys are
        /// `"track"`, `"artist"` and `"album"`. If a timestamp is set, it will be present in the map under key
        /// `"timestamp"`.
        ///
        /// # Example
        /// ```ignore
        /// let scrobble = Scrobble::new("Example Artist", ...);
        /// let scrobble_map = scrobble.as_map();
        /// assert_eq!(scrobble_map.get("artist"), "Example Artist");
        /// ```
        pub fn as_map(&self) -> HashMap<String, String> {
            let mut params = HashMap::new();
            params.insert("track".to_string(), self.track.clone());
            params.insert("artist".to_string(), self.artist.clone());
            params.insert("album".to_string(), self.album.clone());

            if let Some(timestamp) = self.timestamp {
                params.insert("timestamp".to_string(), timestamp.to_string());
            }

            params
        }

        /// Returns the `Scrobble`'s artist name
        pub fn artist(&self) -> &str {
            &self.artist
        }

        /// Returns the `Scrobble`'s track name
        pub fn track(&self) -> &str {
            &self.track
        }

        /// Returns the `Scrobble`'s album name
        pub fn album(&self) -> &str {
            &self.album
        }

    }

    /// Converts from tuple of `&str`s in the form `(artist, track, album)`
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<&(&str, &str, &str)> for Scrobble {
        fn from((artist, track, album): &(&str, &str, &str)) -> Self {
            Scrobble::new(artist, track, album)
        }
    }

    /// Converts from tuple of `String`s in the form `(artist, track, album)`
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<&(String, String, String)> for Scrobble {
        fn from((artist, track, album): &(String, String, String)) -> Self {
            Scrobble::new(artist, track, album)
        }
    }

    /// Converts from vector of `&str` tuples, in the form `(artist, track, album)`.
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<Vec<(&str, &str, &str)>> for ScrobbleBatch {
        fn from(collection: Vec<(&str, &str, &str)>) -> Self {
            let scrobbles: Vec<Scrobble> = collection
                                .iter()
                                .map(Scrobble::from)
                                .collect();

            ScrobbleBatch::from(scrobbles)
        }
    }

    /// Converts from vector of `String` tuples, in the form `(artist, track, album)`.
    ///
    /// Designed to make it easier to cooperate with other track info types.
    impl From<Vec<(String, String, String)>> for ScrobbleBatch {
        fn from(collection: Vec<(String, String, String)>) -> Self {
            let scrobbles: Vec<Scrobble> = collection
                                .iter()
                                .map(Scrobble::from)
                                .collect();

            ScrobbleBatch::from(scrobbles)
        }
    }

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

        #[test]
        fn make_scrobble() {
            let mut scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );
            scrobble.with_timestamp(1337);
            assert_eq!(scrobble.artist(), "foo floyd and the fruit flies");
            assert_eq!(scrobble.track(), "old bananas");
            assert_eq!(scrobble.album(), "old bananas");
            assert_eq!(scrobble.timestamp, Some(1337));
        }

        #[test]
        fn make_scrobble_check_map() {
            let scrobble = Scrobble::new(
                "foo floyd and the fruit flies",
                "old bananas",
                "old bananas",
            );

            let params = scrobble.as_map();
            assert_eq!(params["artist"], "foo floyd and the fruit flies");
            assert_eq!(params["track"], "old bananas");
            assert_eq!(params["album"], "old bananas");
        }
    }
}
<|endoftext|>


<commit_before>use crate::builtins::Blob;
use crate::io::{DecodeError, Wasmbin, WasmbinDecode, WasmbinEncode};
use crate::sections::{Section, StdPayload};
use crate::visit::WasmbinVisit;
use arbitrary::Arbitrary;
use std::cmp::Ordering;

const MAGIC_AND_VERSION: [u8; 8] = [b'\0', b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00];

#[derive(Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, WasmbinVisit)]
pub struct MagicAndVersion;

impl WasmbinEncode for MagicAndVersion {
    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
        w.write_all(&MAGIC_AND_VERSION)
    }
}

impl WasmbinDecode for MagicAndVersion {
    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
        let mut magic_and_version = [0; 8];
        r.read_exact(&mut magic_and_version)?;
        if magic_and_version != MAGIC_AND_VERSION {
            return Err(DecodeError::InvalidMagic);
        }
        Ok(MagicAndVersion)
    }
}

#[derive(Wasmbin, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, WasmbinVisit)]
pub struct Module {
    magic_and_version: MagicAndVersion,
    pub sections: Vec<Section>,
}

impl Module {
    pub fn find_std_section<T: StdPayload>(&self) -> Option<&Blob<T>> {
        self.sections.iter().find_map(|section| section.try_as())
    }

    pub fn find_std_section_mut<T: StdPayload>(&mut self) -> Option<&mut Blob<T>> {
        self.sections
            .iter_mut()
            .find_map(|section| section.try_as_mut())
    }

    pub fn find_or_insert_std_section<T: StdPayload>(
        &mut self,
        insert_callback: impl FnOnce() -> T,
    ) -> &mut Blob<T> {
        let mut index = self.sections.len();
        let mut insert = true;
        for (i, section) in self.sections.iter_mut().enumerate() {
            match section.kind().cmp(&T::KIND) {
                Ordering::Less => continue,
                Ordering::Equal => {
                    // We can't just `return` here due to a bug in rustc:
                    // https://github.com/rust-lang/rust/issues/70255
                    insert = false;
                }
                Ordering::Greater => {}
            }
            index = i;
            break;
        }
        if insert {
            self.sections.insert(index, insert_callback().into());
        }
        unsafe { self.sections.get_unchecked_mut(index) }
            .try_as_mut()
            .unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() })
    }
}
<commit_msg>Expose doc-hidden magic_and_version field<commit_after>use crate::builtins::Blob;
use crate::io::{DecodeError, Wasmbin, WasmbinDecode, WasmbinEncode};
use crate::sections::{Section, StdPayload};
use crate::visit::WasmbinVisit;
use arbitrary::Arbitrary;
use std::cmp::Ordering;

const MAGIC_AND_VERSION: [u8; 8] = [b'\0', b'a', b's', b'm', 0x01, 0x00, 0x00, 0x00];

#[derive(Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, WasmbinVisit)]
pub struct MagicAndVersion;

impl WasmbinEncode for MagicAndVersion {
    fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
        w.write_all(&MAGIC_AND_VERSION)
    }
}

impl WasmbinDecode for MagicAndVersion {
    fn decode(r: &mut impl std::io::Read) -> Result<Self, DecodeError> {
        let mut magic_and_version = [0; 8];
        r.read_exact(&mut magic_and_version)?;
        if magic_and_version != MAGIC_AND_VERSION {
            return Err(DecodeError::InvalidMagic);
        }
        Ok(MagicAndVersion)
    }
}

#[derive(Wasmbin, Debug, Default, Arbitrary, PartialEq, Eq, Hash, Clone, WasmbinVisit)]
pub struct Module {
    #[doc(hidden)]
    pub magic_and_version: MagicAndVersion,
    pub sections: Vec<Section>,
}

impl Module {
    pub fn find_std_section<T: StdPayload>(&self) -> Option<&Blob<T>> {
        self.sections.iter().find_map(|section| section.try_as())
    }

    pub fn find_std_section_mut<T: StdPayload>(&mut self) -> Option<&mut Blob<T>> {
        self.sections
            .iter_mut()
            .find_map(|section| section.try_as_mut())
    }

    pub fn find_or_insert_std_section<T: StdPayload>(
        &mut self,
        insert_callback: impl FnOnce() -> T,
    ) -> &mut Blob<T> {
        let mut index = self.sections.len();
        let mut insert = true;
        for (i, section) in self.sections.iter_mut().enumerate() {
            match section.kind().cmp(&T::KIND) {
                Ordering::Less => continue,
                Ordering::Equal => {
                    // We can't just `return` here due to a bug in rustc:
                    // https://github.com/rust-lang/rust/issues/70255
                    insert = false;
                }
                Ordering::Greater => {}
            }
            index = i;
            break;
        }
        if insert {
            self.sections.insert(index, insert_callback().into());
        }
        unsafe { self.sections.get_unchecked_mut(index) }
            .try_as_mut()
            .unwrap_or_else(|| unsafe { std::hint::unreachable_unchecked() })
    }
}
<|endoftext|>


<commit_before>
use slog::Logger;
use yaml_rust::yaml;
use yaml_rust::Yaml;
use model;

pub fn add_dotfiles_to_config(log: &Logger, config: &Yaml, dotfiles: &[model::DotFile]) -> Yaml {
  let mut new_hash = if let Yaml::Hash(config_hash) = config.clone() {
    config_hash
  } else {
    warn!(log, "Configuration seems invalid. Overwriting it.");
    yaml::Hash::new()
  };
  let new_files = add_dotfiles_to_files(&config["files"], dotfiles);
  new_hash.insert(Yaml::String("files".to_string()), new_files);
  Yaml::Hash(new_hash)
}

fn add_dotfiles_to_files(files: &Yaml, dotfiles: &[model::DotFile]) -> Yaml {
  let mut new_hash = if let Yaml::Hash(files_hash) = files.clone() {
    files_hash
  } else {
    yaml::Hash::new()
  };
  for dotfile in dotfiles {
    let mut prop_hash = yaml::Hash::new();
    prop_hash.insert(Yaml::String("src".to_string()),
                     Yaml::String(dotfile.source.to_string()));
    prop_hash.insert(Yaml::String("type".to_string()),
                     Yaml::String(format!("{:?}", dotfile.dot_file_type)));
    new_hash.insert(Yaml::String(dotfile.target.to_string()),
                    Yaml::Hash(prop_hash));
  }
  Yaml::Hash(new_hash)
}
<commit_msg>test mutate<commit_after>
use slog::Logger;
use yaml_rust::yaml;
use yaml_rust::Yaml;
use model;

pub fn add_dotfiles_to_config(log: &Logger, config: &Yaml, dotfiles: &[model::DotFile]) -> Yaml {
  let mut new_hash = if let Yaml::Hash(config_hash) = config.clone() {
    config_hash
  } else {
    warn!(log, "Configuration seems invalid. Overwriting it.");
    yaml::Hash::new()
  };
  let new_files = add_dotfiles_to_files(&config["files"], dotfiles);
  new_hash.insert(Yaml::String("files".to_string()), new_files);
  Yaml::Hash(new_hash)
}

fn add_dotfiles_to_files(files: &Yaml, dotfiles: &[model::DotFile]) -> Yaml {
  let mut new_hash = if let Yaml::Hash(files_hash) = files.clone() {
    files_hash
  } else {
    yaml::Hash::new()
  };
  for dotfile in dotfiles {
    let mut prop_hash = yaml::Hash::new();
    prop_hash.insert(Yaml::String("src".to_string()),
                     Yaml::String(dotfile.source.to_string()));
    prop_hash.insert(Yaml::String("type".to_string()),
                     Yaml::String(format!("{:?}", dotfile.dot_file_type)));
    new_hash.insert(Yaml::String(dotfile.target.to_string()),
                    Yaml::Hash(prop_hash));
  }
  Yaml::Hash(new_hash)
}
#[cfg(test)]
mod tests {
  use slog;
  use slog_stdlog;
  use slog::DrainExt;
  use yaml_rust::YamlLoader;
  use yaml_rust::{Yaml, YamlEmitter};
  use super::*;
  use spectral::prelude::*;
  use model::{DotFile, DotFileType};

  #[test]
  fn parse_config() {
    let s = "
files:
    ~/.tmux/plugins/tpm: tpm
    ~/.tmux.conf:
        src: tmux.conf
        type: copy
    ~/.vimrc:
        src: vimrc
        type: link
";
    let yaml_documents = YamlLoader::load_from_str(s).unwrap();
    let yaml_config = &yaml_documents[0];
    let logger = slog::Logger::root(slog_stdlog::StdLog.fuse(), o!());
    let new_files = [DotFile {
                       source: "test".to_string(),
                       target: "~/test".to_string(),
                       dot_file_type: DotFileType::LINK,
                     }];
    let new_config = add_dotfiles_to_config(&logger, &yaml_config, &new_files);
    let mut out_str = String::new();
    {
      let mut emitter = YamlEmitter::new(&mut out_str);
      emitter.dump(&new_config).unwrap();
    }

    let expected = "---
files:
  ~/.tmux/plugins/tpm: tpm
  ~/.tmux.conf:
    src: tmux.conf
    type: copy
  ~/.vimrc:
    src: vimrc
    type: link
  ~/test:
    src: test
    type: LINK".to_string();

    assert_that(&out_str).is_equal_to(expected);
  }
}
<|endoftext|>


<commit_before>use super::{LzfResult,LzfError};

pub fn decompress(data: &[u8], out_len: usize) -> LzfResult<Vec<u8>> {
    let mut current_offset = 0;

    let mut output = Vec::with_capacity(out_len);

    let in_len = data.len();

    while current_offset < in_len {
        let mut ctrl = data[current_offset] as usize;
        current_offset += 1;

        if ctrl < (1<<5) {
            ctrl += 1;

            if current_offset+ctrl > in_len {
                return Err(LzfError::DataCorrupted);
            }

            while ctrl > 0 {
                output.push(data[current_offset]);
                current_offset += 1;
                ctrl -= 1;
            }
        } else {
            let mut len = ctrl >> 5;

            let mut ref_offset = (((ctrl & 0x1f) << 8) + 1) as i32;

            if len == 7 {
                len += data[current_offset] as usize;
                current_offset += 1;
            }

            ref_offset += data[current_offset] as i32;
            current_offset += 1;

            let mut ref_pos = (output.len() as i32) - ref_offset;
            if ref_pos < 0 {
                return Err(LzfError::DataCorrupted);
            }

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            let c = output[ref_pos as usize];
            output.push(c);
            ref_pos += 1;

            while len > 0 {
                let c = output[ref_pos as usize];
                output.push(c);
                ref_pos += 1;
                len -= 1;
            }
        }
    }

    Ok(output)
}

#[test]
fn test_native_decompress_lorem() {
    use super::compress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = compress(lorem.as_bytes()).unwrap();

    let decompressed = decompress(&compressed[..], lorem.len()).unwrap();
    assert_eq!(lorem.as_bytes(), &decompressed[..]);
}
<commit_msg>Use all kinds of crazy unsafe code to make it fast<commit_after>use super::{LzfResult,LzfError};
use std::ptr;
use std::mem;

pub fn decompress(data: &[u8], out_len_should: usize) -> LzfResult<Vec<u8>> {
    let mut current_offset = 0;

    // We have sanity checks to not exceed this capacity.
    let mut output = Vec::with_capacity(out_len_should);
    unsafe { output.set_len(out_len_should) };
    let mut out_len : usize = 0;

    let in_len = data.len();

    while current_offset < in_len {
        let mut ctrl = unsafe{*data.get_unchecked(current_offset)} as usize;
        current_offset += 1;

        if ctrl < (1<<5) {
            ctrl += 1;

            if out_len + ctrl > out_len_should {
                return Err(LzfError::BufferTooSmall);
            }

            if current_offset+ctrl > in_len {
                return Err(LzfError::DataCorrupted);
            }

            // We can simply memcpy everything from the input to the output
            unsafe {
                let (src, _) : (*const u8, usize) = mem::transmute(&data[..]);
                let src = src.offset(current_offset as isize);
                let (dst, _) : (*mut u8, usize) = mem::transmute(&output[..]);
                let dst = dst.offset((out_len) as isize);
                ptr::copy_nonoverlapping(src, dst, ctrl);

                current_offset += ctrl;
                out_len += ctrl;
            }
        } else {
            let mut len = ctrl >> 5;

            let mut ref_offset = (((ctrl & 0x1f) << 8) + 1) as i32;

            if len == 7 {
                len += unsafe{*data.get_unchecked(current_offset)} as usize;
                current_offset += 1;

                if current_offset >= in_len {
                    return Err(LzfError::DataCorrupted);
                }
            }

            ref_offset += unsafe{*data.get_unchecked(current_offset)} as i32;
            current_offset += 1;

            if current_offset + len + 2 > out_len_should {
                return Err(LzfError::BufferTooSmall);
            }

            let mut ref_pos = (out_len as i32) - ref_offset;
            if ref_pos < 0 {
                return Err(LzfError::DataCorrupted);
            }

            let c = unsafe{*output.get_unchecked(ref_pos as usize)};
            output[out_len] = c;
            out_len += 1;
            ref_pos += 1;

            let c = unsafe{*output.get_unchecked(ref_pos as usize)};
            output[out_len] = c;
            out_len += 1;
            ref_pos += 1;

            // We can safely use copy_nonoverlapping here,
            // as we know that we only copy data from before our current insertion point.
            unsafe {
                let (src, _) : (*const u8, usize) = mem::transmute(&output[..]);
                let src = src.offset(ref_pos as isize);
                let (dst, _) : (*mut u8, usize) = mem::transmute(&output[..]);
                let dst = dst.offset((out_len) as isize);

                ptr::copy_nonoverlapping(src, dst, len);
                out_len += len;
            }
        }
    }

    // Set the real length now, user might have passed a bigger buffer in the first place.
    unsafe { output.set_len(out_len) };

    Ok(output)
}

#[test]
fn test_native_decompress_lorem() {
    use super::compress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = compress(lorem.as_bytes()).unwrap();

    let decompressed = decompress(&compressed[..], lorem.len()).unwrap();
    assert_eq!(lorem.as_bytes(), &decompressed[..]);

    let decompressed = decompress(&compressed[..], 1000).unwrap();
    assert_eq!(lorem.len(), decompressed.len());
}
<|endoftext|>


<commit_before>use std::mem;

use num::bigint::{self, ToBigInt};
use num::traits::{Zero, One};

use encoding::{Encoding, DecoderTrap};
use encoding::all::ISO_8859_1;

use bertterm::BertTerm;
use error::{Result, BertError};

const BERT_MAGIC_NUMBER: u8 = 131;
const SMALL_INTEGER_EXT: u8 = 97;
const INTEGER_EXT: u8 = 98;
const FLOAT_EXT: u8 = 99;
const ATOM_EXT: u8 = 100;
const SMALL_ATOM_EXT: u8 = 115;
const SMALL_TUPLE_EXT: u8 = 104;
const LARGE_TUPLE_EXT: u8 = 105;
const NIL_EXT: u8 = 106;
const STRING_EXT: u8 = 107;
const LIST_EXT: u8 = 108;
const BINARY_EXT: u8 = 109;
const SMALL_BIG_EXT: u8 = 110;
const LARGE_BIG_EXT: u8 = 111;
const ATOM_UTF8_EXT: u8 = 118;
const SMALL_ATOM_UTF8_EXT: u8 = 119;
const NEW_FLOAT_EXT: u8 = 70;
const MAP_EXT: u8 = 116;

#[derive(Debug)]
pub struct Parser {
    contents: Vec<u8>,
    pos: usize,
}

impl Parser {
    pub fn new(contents: Vec<u8>) -> Parser {
        Parser { contents: contents, pos: 0 }
    }

    pub fn parse(&mut self) -> Result<BertTerm> {
        let () = self.magic_number()?;
        let term = self.bert_term()?;
        if self.eof() {
            return Ok(term);
        } else {
            return Err(BertError::ExtraData(self.pos));
        }
    }

    // Parsers
    fn magic_number(&mut self) -> Result<()> {
        let offset = self.pos;
        let magic = self.eat_u8()?;
        if magic != BERT_MAGIC_NUMBER {
            return Err(BertError::InvalidMagicNumber(offset));
        } else {
            return Ok(());
        }
    }

    fn bert_term(&mut self) -> Result<BertTerm> {
        let offset = self.pos;
        match self.eat_u8()? {
            SMALL_INTEGER_EXT => { self.small_integer() }
            INTEGER_EXT => { self.integer() }
            FLOAT_EXT => { self.old_float() }
            NEW_FLOAT_EXT => { self.new_float() }
            ATOM_EXT => {
                let len = self.eat_u16_be()? as usize;
                self.atom(len)
            }
            SMALL_ATOM_EXT => {
                let len = self.eat_u8()? as usize;
                self.atom(len)
            }
            ATOM_UTF8_EXT => {
                let len = self.eat_u16_be()? as usize;
                self.atom_utf8(len)
            }
            SMALL_ATOM_UTF8_EXT => {
                let len = self.eat_u8()? as usize;
                self.atom_utf8(len)
            }
            SMALL_TUPLE_EXT => {
                let len = self.eat_u8()? as usize;
                self.tuple(len)
            }
            LARGE_TUPLE_EXT => {
                let len = self.eat_u32_be()? as usize;
                self.tuple(len)
            }
            NIL_EXT => { Ok(BertTerm::Nil) }
            LIST_EXT => { self.list() }
            STRING_EXT => { self.string() }
            BINARY_EXT => { self.binary() }
            SMALL_BIG_EXT => {
                let len = self.eat_u8()?;
                self.bigint(len as usize)
            }
            LARGE_BIG_EXT => {
                let len = self.eat_u32_be()?;
                self.bigint(len as usize)
            }
            MAP_EXT => {
                self.map()
            }
            tag => { Err(BertError::InvalidTag(offset, tag)) }
        }
    }

    fn small_integer(&mut self) -> Result<BertTerm> {
        let b = self.eat_u8()?;
        Ok(BertTerm::Int(b as i32))
    }

    fn integer(&mut self) -> Result<BertTerm> {
        let n = self.eat_i32_be()?;
        Ok(BertTerm::Int(n))
    }

    fn old_float(&mut self) -> Result<BertTerm> {
        let offset = self.pos;
        let mut s = String::new();
        while !self.eof() && self.peek()? != 0 {
            s.push(self.eat_char()?);
        }

        while !self.eof() && self.peek()? == 0 {
            let _ = self.eat_u8()?;
        }

        s.parse::<f64>()
            .map_err(|_| BertError::InvalidFloat(offset))
            .map(|f| BertTerm::Float(f))
    }

    fn new_float(&mut self) -> Result<BertTerm> {
        let raw_bytes = self.eat_u64_be()?;
        let f: f64 = unsafe { mem::transmute(raw_bytes) };
        Ok(BertTerm::Float(f))
    }

    fn atom(&mut self, len: usize) -> Result<BertTerm> {
        let offset = self.pos;
        let mut buf: Vec<u8> = vec![0; len];
        let mut is_ascii = true;
        for p in buf.iter_mut() {
            let b = self.eat_u8()?;
            is_ascii = is_ascii && (b < 128);
            *p = b;
        }

        // Optimization: ASCII atoms represent the overwhelming
        // majority of use cases of atoms. When we read the bytes
        // of the atom, we record whether they are all ASCII
        // (i.e., less than 128); if it's the case, we don't
        // need to bother with latin-1 decoding. We use an unsafe
        // method because ASCII strings are guaranteed to be valid
        // UTF-8 strings.
        if is_ascii {
            let s = unsafe { String::from_utf8_unchecked(buf) };
            Ok(BertTerm::Atom(s))
        } else {
            ISO_8859_1.decode(&buf, DecoderTrap::Strict)
                .map(|s| BertTerm::Atom(s))
                .map_err(|_| BertError::InvalidLatin1Atom(offset))
        }
    }

    fn atom_utf8(&mut self, len: usize) -> Result<BertTerm> {
        let offset = self.pos;
        let mut buf: Vec<u8> = vec![0; len];
        let mut is_ascii = true;
        for p in buf.iter_mut() {
            let b = self.eat_u8()?;
            is_ascii = is_ascii && (b < 128);
            *p = b;
        }

        if is_ascii {
            let s = unsafe { String::from_utf8_unchecked(buf) };
            Ok(BertTerm::Atom(s))
        } else {
            String::from_utf8(buf)
                .map(|s| BertTerm::Atom(s))
                .map_err(|_| BertError::InvalidUTF8Atom(offset))
        }
    }

    fn tuple(&mut self, len: usize) -> Result<BertTerm> {
        let mut terms = Vec::with_capacity(len);
        for _ in 0 .. len {
            terms.push(self.bert_term()?);
        }
        Ok(BertTerm::Tuple(terms))
    }

    fn string(&mut self) -> Result<BertTerm> {
        let len = self.eat_u16_be()?;
        let bytes = self.string_content(len as usize)?;
        Ok(BertTerm::String(bytes))
    }

    fn binary(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()?;
        let bytes = self.string_content(len as usize)?;
        Ok(BertTerm::Binary(bytes))
    }

    fn string_content(&mut self, len: usize) -> Result<Vec<u8>> {
        let mut bytes: Vec<u8> = vec![0; len];
        for p in bytes.iter_mut() {
            *p = self.eat_u8()?;
        }
        return Ok(bytes);
    }

    fn list(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()?;
        let mut terms = Vec::with_capacity(len as usize + 1);
        for _ in 0 .. len {
            terms.push(self.bert_term()?);
        }
        let tail = self.bert_term()?;
        match tail {
            BertTerm::Nil => (),
            last_term => { terms.push(last_term); }
        };
        Ok(BertTerm::List(terms))
    }

    fn bigint(&mut self, len: usize) -> Result<BertTerm> {
        let sign = self.eat_u8()?;
        let mut sum: bigint::BigInt = Zero::zero();
        let mut pos: bigint::BigInt = One::one();
        for _ in 0 .. len {
            let d = self.eat_u8()?;
            let t = &pos * &(d.to_bigint().unwrap());
            sum = sum + &t;
            pos = pos * (256).to_bigint().unwrap();
        }
        if sign == 1 {
            sum = -sum;
        }
        Ok(BertTerm::BigInt(sum))
    }

    // TODO(vfoley): ensure no duplicate keys
    fn map(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()? as usize;
        let mut keys = Vec::with_capacity(len);
        let mut vals = Vec::with_capacity(len);
        for _ in 0 .. len {
            keys.push(self.bert_term()?);
            vals.push(self.bert_term()?);
        }
        Ok(BertTerm::Map(keys, vals))
    }

    // Low-level parsing methods
    fn eof(&self) -> bool {
        self.pos >= self.contents.len()
    }

    fn peek(&self) -> Result<u8> {
        if self.eof() {
            return Err(BertError::EOF(self.pos));
        } else {
            return Ok(self.contents[self.pos]);
        }
    }

    fn eat_u8(&mut self) -> Result<u8> {
        let b = self.peek()?;
        self.pos += 1;
        return Ok(b);
    }

    fn eat_char(&mut self) -> Result<char> {
        let b = self.eat_u8()?;
        return Ok(b as char);
    }

    fn eat_u16_be(&mut self) -> Result<u16> {
        let b0 = self.eat_u8()? as u16;
        let b1 = self.eat_u8()? as u16;
        return Ok((b0 << 8) + b1)
    }

    fn eat_i32_be(&mut self) -> Result<i32> {
        let b0 = self.eat_u8()? as i32;
        let b1 = self.eat_u8()? as i32;
        let b2 = self.eat_u8()? as i32;
        let b3 = self.eat_u8()? as i32;
        return Ok((b0 << 24) + (b1 << 16) + (b2 << 8) + b3)
    }

    fn eat_u32_be(&mut self) -> Result<u32> {
        let b0 = self.eat_u8()? as u32;
        let b1 = self.eat_u8()? as u32;
        let b2 = self.eat_u8()? as u32;
        let b3 = self.eat_u8()? as u32;
        return Ok((b0 << 24) + (b1 << 16) + (b2 << 8) + b3)
    }

    fn eat_u64_be(&mut self) -> Result<u64> {
        let mut n: u64 = 0;
        n += (self.eat_u8()? as u64) << 56;
        n += (self.eat_u8()? as u64) << 48;
        n += (self.eat_u8()? as u64) << 40;
        n += (self.eat_u8()? as u64) << 32;
        n += (self.eat_u8()? as u64) << 24;
        n += (self.eat_u8()? as u64) << 16;
        n += (self.eat_u8()? as u64) << 8;
        n += self.eat_u8()? as u64;
        return Ok(n);
    }
}
<commit_msg>Apply micro-optimization to atoms<commit_after>use std::mem;

use num::bigint::{self, ToBigInt};
use num::traits::{Zero, One};

use encoding::{Encoding, DecoderTrap};
use encoding::all::ISO_8859_1;

use bertterm::BertTerm;
use error::{Result, BertError};

const BERT_MAGIC_NUMBER: u8 = 131;
const SMALL_INTEGER_EXT: u8 = 97;
const INTEGER_EXT: u8 = 98;
const FLOAT_EXT: u8 = 99;
const ATOM_EXT: u8 = 100;
const SMALL_ATOM_EXT: u8 = 115;
const SMALL_TUPLE_EXT: u8 = 104;
const LARGE_TUPLE_EXT: u8 = 105;
const NIL_EXT: u8 = 106;
const STRING_EXT: u8 = 107;
const LIST_EXT: u8 = 108;
const BINARY_EXT: u8 = 109;
const SMALL_BIG_EXT: u8 = 110;
const LARGE_BIG_EXT: u8 = 111;
const ATOM_UTF8_EXT: u8 = 118;
const SMALL_ATOM_UTF8_EXT: u8 = 119;
const NEW_FLOAT_EXT: u8 = 70;
const MAP_EXT: u8 = 116;

#[derive(Debug)]
pub struct Parser {
    contents: Vec<u8>,
    pos: usize,
}

impl Parser {
    pub fn new(contents: Vec<u8>) -> Parser {
        Parser { contents: contents, pos: 0 }
    }

    pub fn parse(&mut self) -> Result<BertTerm> {
        let () = self.magic_number()?;
        let term = self.bert_term()?;
        if self.eof() {
            return Ok(term);
        } else {
            return Err(BertError::ExtraData(self.pos));
        }
    }

    // Parsers
    fn magic_number(&mut self) -> Result<()> {
        let offset = self.pos;
        let magic = self.eat_u8()?;
        if magic != BERT_MAGIC_NUMBER {
            return Err(BertError::InvalidMagicNumber(offset));
        } else {
            return Ok(());
        }
    }

    fn bert_term(&mut self) -> Result<BertTerm> {
        let offset = self.pos;
        match self.eat_u8()? {
            SMALL_INTEGER_EXT => { self.small_integer() }
            INTEGER_EXT => { self.integer() }
            FLOAT_EXT => { self.old_float() }
            NEW_FLOAT_EXT => { self.new_float() }
            ATOM_EXT => {
                let len = self.eat_u16_be()? as usize;
                self.atom(len)
            }
            SMALL_ATOM_EXT => {
                let len = self.eat_u8()? as usize;
                self.atom(len)
            }
            ATOM_UTF8_EXT => {
                let len = self.eat_u16_be()? as usize;
                self.atom_utf8(len)
            }
            SMALL_ATOM_UTF8_EXT => {
                let len = self.eat_u8()? as usize;
                self.atom_utf8(len)
            }
            SMALL_TUPLE_EXT => {
                let len = self.eat_u8()? as usize;
                self.tuple(len)
            }
            LARGE_TUPLE_EXT => {
                let len = self.eat_u32_be()? as usize;
                self.tuple(len)
            }
            NIL_EXT => { Ok(BertTerm::Nil) }
            LIST_EXT => { self.list() }
            STRING_EXT => { self.string() }
            BINARY_EXT => { self.binary() }
            SMALL_BIG_EXT => {
                let len = self.eat_u8()?;
                self.bigint(len as usize)
            }
            LARGE_BIG_EXT => {
                let len = self.eat_u32_be()?;
                self.bigint(len as usize)
            }
            MAP_EXT => {
                self.map()
            }
            tag => { Err(BertError::InvalidTag(offset, tag)) }
        }
    }

    fn small_integer(&mut self) -> Result<BertTerm> {
        let b = self.eat_u8()?;
        Ok(BertTerm::Int(b as i32))
    }

    fn integer(&mut self) -> Result<BertTerm> {
        let n = self.eat_i32_be()?;
        Ok(BertTerm::Int(n))
    }

    fn old_float(&mut self) -> Result<BertTerm> {
        let offset = self.pos;
        let mut s = String::new();
        while !self.eof() && self.peek()? != 0 {
            s.push(self.eat_char()?);
        }

        while !self.eof() && self.peek()? == 0 {
            let _ = self.eat_u8()?;
        }

        s.parse::<f64>()
            .map_err(|_| BertError::InvalidFloat(offset))
            .map(|f| BertTerm::Float(f))
    }

    fn new_float(&mut self) -> Result<BertTerm> {
        let raw_bytes = self.eat_u64_be()?;
        let f: f64 = unsafe { mem::transmute(raw_bytes) };
        Ok(BertTerm::Float(f))
    }

    fn atom(&mut self, len: usize) -> Result<BertTerm> {
        let offset = self.pos;
        let mut bytes: Vec<u8> = Vec::with_capacity(len);
        let mut is_ascii = true;
        for _ in 0 .. len {
            let b = self.eat_u8()?;
            is_ascii = is_ascii && (b < 128);
            bytes.push(b);
        }

        // Optimization: ASCII atoms represent the overwhelming
        // majority of use cases of atoms. When we read the bytes
        // of the atom, we record whether they are all ASCII
        // (i.e., small than 128); if it's the case, we don't
        // need to bother with latin-1 decoding. We use an unsafe
        // method because ASCII strings are guaranteed to be valid
        // UTF-8 strings.
        if is_ascii {
            let s = unsafe { String::from_utf8_unchecked(bytes) };
            Ok(BertTerm::Atom(s))
        } else {
            ISO_8859_1.decode(&bytes, DecoderTrap::Strict)
                .map(|s| BertTerm::Atom(s))
                .map_err(|_| BertError::InvalidLatin1Atom(offset))
        }
    }

    fn atom_utf8(&mut self, len: usize) -> Result<BertTerm> {
        let offset = self.pos;
        let mut buf = Vec::with_capacity(len);
        for _ in 0 .. len {
            buf.push(self.eat_u8()?);
        }
        String::from_utf8(buf)
            .map(|s| BertTerm::Atom(s))
            .map_err(|_| BertError::InvalidUTF8Atom(offset))
    }

    fn tuple(&mut self, len: usize) -> Result<BertTerm> {
        let mut terms = Vec::with_capacity(len);
        for _ in 0 .. len {
            terms.push(self.bert_term()?);
        }
        Ok(BertTerm::Tuple(terms))
    }

    fn string(&mut self) -> Result<BertTerm> {
        let len = self.eat_u16_be()?;
        let mut bytes = Vec::with_capacity(len as usize);
        for _ in 0 .. len {
            bytes.push(self.eat_u8()?);
        }
        Ok(BertTerm::String(bytes))
    }

    fn binary(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()?;
        let mut bytes = Vec::with_capacity(len as usize);
        for _ in 0 .. len {
            bytes.push(self.eat_u8()?);
        }
        Ok(BertTerm::Binary(bytes))
    }

    fn list(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()?;
        let mut terms = Vec::with_capacity(len as usize + 1);
        for _ in 0 .. len {
            terms.push(self.bert_term()?);
        }
        let tail = self.bert_term()?;
        match tail {
            BertTerm::Nil => (),
            last_term => { terms.push(last_term); }
        };
        Ok(BertTerm::List(terms))
    }

    fn bigint(&mut self, len: usize) -> Result<BertTerm> {
        let sign = self.eat_u8()?;
        let mut sum: bigint::BigInt = Zero::zero();
        let mut pos: bigint::BigInt = One::one();
        for _ in 0 .. len {
            let d = self.eat_u8()?;
            let t = &pos * &(d.to_bigint().unwrap());
            sum = sum + &t;
            pos = pos * (256).to_bigint().unwrap();
        }
        if sign == 1 {
            sum = -sum;
        }
        Ok(BertTerm::BigInt(sum))
    }

    // TODO(vfoley): ensure no duplicate keys
    fn map(&mut self) -> Result<BertTerm> {
        let len = self.eat_u32_be()? as usize;
        let mut keys = Vec::with_capacity(len);
        let mut vals = Vec::with_capacity(len);
        for _ in 0 .. len {
            keys.push(self.bert_term()?);
            vals.push(self.bert_term()?);
        }
        Ok(BertTerm::Map(keys, vals))
    }

    // Low-level parsing methods
    fn eof(&self) -> bool {
        self.pos >= self.contents.len()
    }

    fn peek(&self) -> Result<u8> {
        if self.eof() {
            return Err(BertError::EOF(self.pos));
        } else {
            return Ok(self.contents[self.pos]);
        }
    }

    fn eat_u8(&mut self) -> Result<u8> {
        let b = self.peek()?;
        self.pos += 1;
        return Ok(b);
    }

    fn eat_char(&mut self) -> Result<char> {
        let b = self.eat_u8()?;
        return Ok(b as char);
    }

    fn eat_u16_be(&mut self) -> Result<u16> {
        let b0 = self.eat_u8()? as u16;
        let b1 = self.eat_u8()? as u16;
        return Ok((b0 << 8) + b1)
    }

    fn eat_i32_be(&mut self) -> Result<i32> {
        let b0 = self.eat_u8()? as i32;
        let b1 = self.eat_u8()? as i32;
        let b2 = self.eat_u8()? as i32;
        let b3 = self.eat_u8()? as i32;
        return Ok((b0 << 24) + (b1 << 16) + (b2 << 8) + b3)
    }

    fn eat_u32_be(&mut self) -> Result<u32> {
        let b0 = self.eat_u8()? as u32;
        let b1 = self.eat_u8()? as u32;
        let b2 = self.eat_u8()? as u32;
        let b3 = self.eat_u8()? as u32;
        return Ok((b0 << 24) + (b1 << 16) + (b2 << 8) + b3)
    }

    fn eat_u64_be(&mut self) -> Result<u64> {
        let mut n: u64 = 0;
        n += (self.eat_u8()? as u64) << 56;
        n += (self.eat_u8()? as u64) << 48;
        n += (self.eat_u8()? as u64) << 40;
        n += (self.eat_u8()? as u64) << 32;
        n += (self.eat_u8()? as u64) << 24;
        n += (self.eat_u8()? as u64) << 16;
        n += (self.eat_u8()? as u64) << 8;
        n += self.eat_u8()? as u64;
        return Ok(n);
    }
}
<|endoftext|>


<commit_before>
/*
 * Interpreter for Basic C like language
 * Parser Module
 */

use lexer;

pub enum TreeNode {
    OperatorNode {leftNode: Option<Box<TreeNode>>, rightNode: Option<Box<TreeNode>>},
    NumberNode {value: i64},
    VariableNode(String),
    StringNode(String)
}

pub fn Initiate(tokens: Vec<lexer::Token>) -> bool {

    parseIt(tokens);
    return false;
}

fn parseIt(tokenStream: Vec<lexer::Token>) {

    let tokenCount = tokenStream.len();
    println!("{}", tokenCount);
}
<commit_msg>Small Update for Parser<commit_after>
/*
 * Interpreter for Basic C like language
 * Parser Module
 */

use lexer;

pub enum TreeNode {
    OperatorNode {leftNode: Option<Box<TreeNode>>, rightNode: Option<Box<TreeNode>>},
    NumberNode {value: i64},
    VariableNode(String),
    StringNode(String)
}

pub fn Initiate(tokens: Vec<lexer::Token>) -> bool {
    parseIt(&tokens);
    false
}

fn parseIt(tokenStream: &Vec<lexer::Token>) {
    let tokenCount = tokenStream.len();
    let mut stack : Vec<String> = Vec::new();
    let mut stackCount = 0;
    let mut i = 0;

    println!("Token Count: {}", tokenCount);

    while i < tokenCount {
        //println!("{:?}", tokenStream[i]);
        i += 1;

    }
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate encoding;

use std::borrow::Cow;
use std::default::Default;
use std::error;
use std::fmt;
use std::io;

use self::byteorder::{BigEndian, ByteOrder, LittleEndian};
use self::encoding::label::encoding_from_whatwg_label;
use self::encoding::types::DecoderTrap::Strict;
use self::encoding::types::EncodingRef;

use super::plurals::{Ast, Resolver};
use super::{Catalog, Message};
use metadata::parse_metadata;

#[allow(non_upper_case_globals)]
static utf8_encoding: EncodingRef = &encoding::codec::utf_8::UTF8Encoding;

/// Represents an error encountered while parsing an MO file.
#[derive(Debug)]
pub enum Error {
    /// An incorrect magic number has been encountered
    BadMagic,
    /// An invalid byte sequence for the given encoding has been encountered
    DecodingError,
    /// An unexpected EOF occured
    Eof,
    /// An I/O error occured
    Io(io::Error),
    /// Incorrect syntax encountered while parsing the meta information
    MalformedMetadata,
    /// Meta information string was not the first string in the catalog
    MisplacedMetadata,
    /// Invalid Plural-Forms metadata
    PluralParsing,
    /// An unknown encoding was specified in the metadata
    UnknownEncoding,
}
use Error::*;

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic => "bad magic number",
            DecodingError => "invalid byte sequence in a string",
            Eof => "unxpected end of file",
            Io(ref err) => err.description(),
            MalformedMetadata => "metadata syntax error",
            MisplacedMetadata => "misplaced metadata",
            UnknownEncoding => "unknown encoding specified",
            PluralParsing => "invalid plural expression",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let self_err: &error::Error = self;
        write!(fmt, "{}", self_err.description())
    }
}

impl From<io::Error> for Error {
    fn from(inner: io::Error) -> Error {
        Io(inner)
    }
}

impl From<Cow<'static, str>> for Error {
    fn from(_: Cow<'static, str>) -> Error {
        DecodingError
    }
}

/// ParseOptions allows setting options for parsing MO catalogs.
///
/// # Examples
/// ```ignore
/// use std::fs::File;
///
/// extern crate encoding;
/// use encoding::all::ISO_8859_1;
///
/// let file = File::open("french.mo").unwrap();
/// let catalog = ParseOptions::new().force_encoding(ISO_8859_1).parse(file).unwrap();
/// ```
#[allow(missing_debug_implementations)]
#[derive(Default)]
pub struct ParseOptions {
    force_encoding: Option<EncodingRef>,
    force_plural: Option<fn(u64) -> usize>,
}

impl ParseOptions {
    /// Returns a new instance of ParseOptions with default options.
    pub fn new() -> Self {
        Default::default()
    }

    /// Tries to parse the catalog from the given reader using the specified options.
    pub fn parse<R: io::Read>(self, reader: R) -> Result<Catalog, Error> {
        parse_catalog(reader, self)
    }

    /// Forces a use of a specific encoding
    /// when parsing strings from a catalog.
    /// If this option is not enabled,
    /// the parser tries to use the encoding specified in the metadata
    /// or UTF-8 if metadata is non-existent.
    pub fn force_encoding(mut self, encoding: EncodingRef) -> Self {
        self.force_encoding = Some(encoding);
        self
    }

    /// Forces a use of the given plural formula
    /// for deciding the proper plural form for a message.
    /// If this option is not enabled,
    /// the parser uses the default formula
    /// (`n != 1`).
    pub fn force_plural(mut self, plural: fn(u64) -> usize) -> Self {
        self.force_plural = Some(plural);
        self
    }
}

/// According to the given magic number of a MO file,
/// returns the function which reads a `u32` in the relevant endianness.
fn get_read_u32_fn(magic: &[u8]) -> Option<fn(&[u8]) -> u32> {
    if magic == [0xde, 0x12, 0x04, 0x95] {
        Some(LittleEndian::read_u32)
    } else if magic == [0x95, 0x04, 0x12, 0xde] {
        Some(BigEndian::read_u32)
    } else {
        None
    }
}

pub fn parse_catalog<'a, R: io::Read>(mut file: R, opts: ParseOptions) -> Result<Catalog, Error> {
    let mut contents = vec![];
    let n = try!(file.read_to_end(&mut contents));
    if n < 28 {
        return Err(Eof);
    }

    let read_u32 = match get_read_u32_fn(&contents[0..4]) {
        Some(f) => f,
        None => return Err(BadMagic),
    };

    // ignore hashing tables (bytes at 20..28)
    let num_strings = read_u32(&contents[8..12]) as usize;
    let mut off_otable = read_u32(&contents[12..16]) as usize;
    let mut off_ttable = read_u32(&contents[16..20]) as usize;
    if n < off_otable || n < off_ttable {
        return Err(Eof);
    }

    let mut catalog = Catalog::new();
    if let Some(f) = opts.force_plural {
        catalog.resolver = Resolver::Function(f);
    }
    let mut encoding = opts.force_encoding.unwrap_or(utf8_encoding);

    for i in 0..num_strings {
        // Parse the original string
        if n < off_otable + 8 {
            return Err(Eof);
        }
        let len = read_u32(&contents[off_otable..off_otable + 4]) as usize;
        let off = read_u32(&contents[off_otable + 4..off_otable + 8]) as usize;
        // +1 compensates for the ending NUL byte which is not included in length
        if n < off + len + 1 {
            return Err(Eof);
        }
        let mut original = &contents[off..off + len + 1];
        // check for context
        let context = match original.iter().position(|x| *x == 4) {
            Some(idx) => {
                let ctx = &original[..idx];
                original = &original[idx + 1..];
                Some(try!(encoding.decode(ctx, Strict)))
            }
            None => None,
        };
        // extract msg_id singular, ignoring the plural
        let id = match original
            .iter()
            .position(|x| *x == 0)
            .map(|i| &original[..i])
        {
            Some(b) => try!(encoding.decode(b, Strict)),
            None => return Err(Eof),
        };
        if id == "" && i != 0 {
            return Err(MisplacedMetadata);
        }

        // Parse the translation strings
        if n < off_ttable + 8 {
            return Err(Eof);
        }
        let len = read_u32(&contents[off_ttable..off_ttable + 4]) as usize;
        let off = read_u32(&contents[off_ttable + 4..off_ttable + 8]) as usize;
        // +1 compensates for the ending NUL byte which is not included in length
        if n < off + len + 1 {
            return Err(Eof);
        }
        let translated = try!(
            (&contents[off..off + len])
                .split(|x| *x == 0)
                .map(|b| encoding.decode(b, Strict))
                .collect::<Result<Vec<_>, _>>()
        );
        if id == "" {
            let map = parse_metadata(&*translated[0])?;
            if let (Some(c), None) = (map.charset(), opts.force_encoding) {
                encoding = match encoding_from_whatwg_label(c) {
                    Some(enc_ref) => enc_ref,
                    None => return Err(UnknownEncoding),
                }
            }
            if opts.force_plural.is_none() {
                if let Some(p) = map.plural_forms().1 {
                    catalog.resolver = Ast::parse(p).map(Resolver::Expr)?;
                }
            }
        }

        catalog.insert(Message::new(id, context, translated));

        off_otable += 8;
        off_ttable += 8;
    }

    Ok(catalog)
}

/// The default plural resolver.
///
/// It will be used if not `Plural-Forms` header is found in the .mo file, and if
/// `ParseOptions::force_plural` was not called.
///
/// It is valid for English and similar languages: plural will be used for any quantity
/// different of 1.
pub fn default_resolver(n: u64) -> usize {
    if n == 1 {
        0
    } else {
        1
    }
}

#[test]
fn test_get_read_u32_fn() {
    use std::mem;

    assert!(get_read_u32_fn(&[]).is_none());
    assert!(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95, 0x00]).is_none());

    {
        let le_ptr: *const ();
        let ret_ptr;
        unsafe {
            le_ptr = mem::transmute(LittleEndian::read_u32 as usize);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95]).unwrap());
        }
        assert_eq!(le_ptr, ret_ptr);
    }

    {
        let be_ptr: *const ();
        let ret_ptr;
        unsafe {
            be_ptr = mem::transmute(BigEndian::read_u32 as usize);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0x95, 0x04, 0x12, 0xde]).unwrap());
        }
        assert_eq!(be_ptr, ret_ptr);
    }
}

#[test]
fn test_parse_catalog() {
    macro_rules! assert_variant {
        ($value:expr, $variant:path) => {
            match $value {
                $variant => (),
                _ => panic!("Expected {:?}, got {:?}", $variant, $value),
            }
        };
    }

    let fluff = [0; 24]; // zeros to pad our magic test cases to satisfy the length requirements

    {
        let mut reader = vec![1u8, 2, 3];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..], ParseOptions::new()).unwrap_err();
        assert_variant!(err, Eof);
    }

    {
        let mut reader = vec![1u8, 2, 3, 4];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..], ParseOptions::new()).unwrap_err();
        assert_variant!(err, BadMagic);
    }

    {
        let mut reader = vec![0x95, 0x04, 0x12, 0xde];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..], ParseOptions::new()).is_ok());
    }

    {
        let mut reader = vec![0xde, 0x12, 0x04, 0x95];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..], ParseOptions::new()).is_ok());
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/1.mo");
        let catalog = parse_catalog(reader, ParseOptions::new()).unwrap();
        assert_eq!(catalog.strings.len(), 1);
        assert_eq!(
            catalog.strings["this is context\x04Text"],
            Message::new("Text", Some("this is context"), vec!["Tekstas", "Tekstai"])
        );
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/2.mo");
        let catalog = parse_catalog(reader, ParseOptions::new()).unwrap();
        assert_eq!(catalog.strings.len(), 2);
        assert_eq!(
            catalog.strings["Image"],
            Message::new("Image", None, vec!["Nuotrauka", "Nuotraukos"])
        );
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/invalid_utf8.mo");
        let err = parse_catalog(reader, ParseOptions::new()).unwrap_err();
        assert_variant!(err, DecodingError);
    }
}
<commit_msg>Replace existing try! macros with question mark operators<commit_after>extern crate byteorder;
extern crate encoding;

use std::borrow::Cow;
use std::default::Default;
use std::error;
use std::fmt;
use std::io;

use self::byteorder::{BigEndian, ByteOrder, LittleEndian};
use self::encoding::label::encoding_from_whatwg_label;
use self::encoding::types::DecoderTrap::Strict;
use self::encoding::types::EncodingRef;

use super::plurals::{Ast, Resolver};
use super::{Catalog, Message};
use metadata::parse_metadata;

#[allow(non_upper_case_globals)]
static utf8_encoding: EncodingRef = &encoding::codec::utf_8::UTF8Encoding;

/// Represents an error encountered while parsing an MO file.
#[derive(Debug)]
pub enum Error {
    /// An incorrect magic number has been encountered
    BadMagic,
    /// An invalid byte sequence for the given encoding has been encountered
    DecodingError,
    /// An unexpected EOF occured
    Eof,
    /// An I/O error occured
    Io(io::Error),
    /// Incorrect syntax encountered while parsing the meta information
    MalformedMetadata,
    /// Meta information string was not the first string in the catalog
    MisplacedMetadata,
    /// Invalid Plural-Forms metadata
    PluralParsing,
    /// An unknown encoding was specified in the metadata
    UnknownEncoding,
}
use Error::*;

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            BadMagic => "bad magic number",
            DecodingError => "invalid byte sequence in a string",
            Eof => "unxpected end of file",
            Io(ref err) => err.description(),
            MalformedMetadata => "metadata syntax error",
            MisplacedMetadata => "misplaced metadata",
            UnknownEncoding => "unknown encoding specified",
            PluralParsing => "invalid plural expression",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let self_err: &error::Error = self;
        write!(fmt, "{}", self_err.description())
    }
}

impl From<io::Error> for Error {
    fn from(inner: io::Error) -> Error {
        Io(inner)
    }
}

impl From<Cow<'static, str>> for Error {
    fn from(_: Cow<'static, str>) -> Error {
        DecodingError
    }
}

/// ParseOptions allows setting options for parsing MO catalogs.
///
/// # Examples
/// ```ignore
/// use std::fs::File;
///
/// extern crate encoding;
/// use encoding::all::ISO_8859_1;
///
/// let file = File::open("french.mo").unwrap();
/// let catalog = ParseOptions::new().force_encoding(ISO_8859_1).parse(file).unwrap();
/// ```
#[allow(missing_debug_implementations)]
#[derive(Default)]
pub struct ParseOptions {
    force_encoding: Option<EncodingRef>,
    force_plural: Option<fn(u64) -> usize>,
}

impl ParseOptions {
    /// Returns a new instance of ParseOptions with default options.
    pub fn new() -> Self {
        Default::default()
    }

    /// Tries to parse the catalog from the given reader using the specified options.
    pub fn parse<R: io::Read>(self, reader: R) -> Result<Catalog, Error> {
        parse_catalog(reader, self)
    }

    /// Forces a use of a specific encoding
    /// when parsing strings from a catalog.
    /// If this option is not enabled,
    /// the parser tries to use the encoding specified in the metadata
    /// or UTF-8 if metadata is non-existent.
    pub fn force_encoding(mut self, encoding: EncodingRef) -> Self {
        self.force_encoding = Some(encoding);
        self
    }

    /// Forces a use of the given plural formula
    /// for deciding the proper plural form for a message.
    /// If this option is not enabled,
    /// the parser uses the default formula
    /// (`n != 1`).
    pub fn force_plural(mut self, plural: fn(u64) -> usize) -> Self {
        self.force_plural = Some(plural);
        self
    }
}

/// According to the given magic number of a MO file,
/// returns the function which reads a `u32` in the relevant endianness.
fn get_read_u32_fn(magic: &[u8]) -> Option<fn(&[u8]) -> u32> {
    if magic == [0xde, 0x12, 0x04, 0x95] {
        Some(LittleEndian::read_u32)
    } else if magic == [0x95, 0x04, 0x12, 0xde] {
        Some(BigEndian::read_u32)
    } else {
        None
    }
}

pub fn parse_catalog<'a, R: io::Read>(mut file: R, opts: ParseOptions) -> Result<Catalog, Error> {
    let mut contents = vec![];
    let n = file.read_to_end(&mut contents)?;
    if n < 28 {
        return Err(Eof);
    }

    let read_u32 = match get_read_u32_fn(&contents[0..4]) {
        Some(f) => f,
        None => return Err(BadMagic),
    };

    // ignore hashing tables (bytes at 20..28)
    let num_strings = read_u32(&contents[8..12]) as usize;
    let mut off_otable = read_u32(&contents[12..16]) as usize;
    let mut off_ttable = read_u32(&contents[16..20]) as usize;
    if n < off_otable || n < off_ttable {
        return Err(Eof);
    }

    let mut catalog = Catalog::new();
    if let Some(f) = opts.force_plural {
        catalog.resolver = Resolver::Function(f);
    }
    let mut encoding = opts.force_encoding.unwrap_or(utf8_encoding);

    for i in 0..num_strings {
        // Parse the original string
        if n < off_otable + 8 {
            return Err(Eof);
        }
        let len = read_u32(&contents[off_otable..off_otable + 4]) as usize;
        let off = read_u32(&contents[off_otable + 4..off_otable + 8]) as usize;
        // +1 compensates for the ending NUL byte which is not included in length
        if n < off + len + 1 {
            return Err(Eof);
        }
        let mut original = &contents[off..off + len + 1];
        // check for context
        let context = match original.iter().position(|x| *x == 4) {
            Some(idx) => {
                let ctx = &original[..idx];
                original = &original[idx + 1..];
                Some(encoding.decode(ctx, Strict)?)
            }
            None => None,
        };
        // extract msg_id singular, ignoring the plural
        let id = match original
            .iter()
            .position(|x| *x == 0)
            .map(|i| &original[..i])
        {
            Some(b) => encoding.decode(b, Strict)?,
            None => return Err(Eof),
        };
        if id == "" && i != 0 {
            return Err(MisplacedMetadata);
        }

        // Parse the translation strings
        if n < off_ttable + 8 {
            return Err(Eof);
        }
        let len = read_u32(&contents[off_ttable..off_ttable + 4]) as usize;
        let off = read_u32(&contents[off_ttable + 4..off_ttable + 8]) as usize;
        // +1 compensates for the ending NUL byte which is not included in length
        if n < off + len + 1 {
            return Err(Eof);
        }
        let translated = contents[off..off + len]
            .split(|x| *x == 0)
            .map(|b| encoding.decode(b, Strict))
            .collect::<Result<Vec<_>, _>>()?;
        if id == "" {
            let map = parse_metadata(&*translated[0])?;
            if let (Some(c), None) = (map.charset(), opts.force_encoding) {
                encoding = match encoding_from_whatwg_label(c) {
                    Some(enc_ref) => enc_ref,
                    None => return Err(UnknownEncoding),
                }
            }
            if opts.force_plural.is_none() {
                if let Some(p) = map.plural_forms().1 {
                    catalog.resolver = Ast::parse(p).map(Resolver::Expr)?;
                }
            }
        }

        catalog.insert(Message::new(id, context, translated));

        off_otable += 8;
        off_ttable += 8;
    }

    Ok(catalog)
}

/// The default plural resolver.
///
/// It will be used if not `Plural-Forms` header is found in the .mo file, and if
/// `ParseOptions::force_plural` was not called.
///
/// It is valid for English and similar languages: plural will be used for any quantity
/// different of 1.
pub fn default_resolver(n: u64) -> usize {
    if n == 1 {
        0
    } else {
        1
    }
}

#[test]
fn test_get_read_u32_fn() {
    use std::mem;

    assert!(get_read_u32_fn(&[]).is_none());
    assert!(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95, 0x00]).is_none());

    {
        let le_ptr: *const ();
        let ret_ptr;
        unsafe {
            le_ptr = mem::transmute(LittleEndian::read_u32 as usize);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0xde, 0x12, 0x04, 0x95]).unwrap());
        }
        assert_eq!(le_ptr, ret_ptr);
    }

    {
        let be_ptr: *const ();
        let ret_ptr;
        unsafe {
            be_ptr = mem::transmute(BigEndian::read_u32 as usize);
            ret_ptr = mem::transmute(get_read_u32_fn(&[0x95, 0x04, 0x12, 0xde]).unwrap());
        }
        assert_eq!(be_ptr, ret_ptr);
    }
}

#[test]
fn test_parse_catalog() {
    macro_rules! assert_variant {
        ($value:expr, $variant:path) => {
            match $value {
                $variant => (),
                _ => panic!("Expected {:?}, got {:?}", $variant, $value),
            }
        };
    }

    let fluff = [0; 24]; // zeros to pad our magic test cases to satisfy the length requirements

    {
        let mut reader = vec![1u8, 2, 3];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..], ParseOptions::new()).unwrap_err();
        assert_variant!(err, Eof);
    }

    {
        let mut reader = vec![1u8, 2, 3, 4];
        reader.extend(fluff.iter().cloned());
        let err = parse_catalog(&reader[..], ParseOptions::new()).unwrap_err();
        assert_variant!(err, BadMagic);
    }

    {
        let mut reader = vec![0x95, 0x04, 0x12, 0xde];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..], ParseOptions::new()).is_ok());
    }

    {
        let mut reader = vec![0xde, 0x12, 0x04, 0x95];
        reader.extend(fluff.iter().cloned());
        assert!(parse_catalog(&reader[..], ParseOptions::new()).is_ok());
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/1.mo");
        let catalog = parse_catalog(reader, ParseOptions::new()).unwrap();
        assert_eq!(catalog.strings.len(), 1);
        assert_eq!(
            catalog.strings["this is context\x04Text"],
            Message::new("Text", Some("this is context"), vec!["Tekstas", "Tekstai"])
        );
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/2.mo");
        let catalog = parse_catalog(reader, ParseOptions::new()).unwrap();
        assert_eq!(catalog.strings.len(), 2);
        assert_eq!(
            catalog.strings["Image"],
            Message::new("Image", None, vec!["Nuotrauka", "Nuotraukos"])
        );
    }

    {
        let reader: &[u8] = include_bytes!("../test_cases/invalid_utf8.mo");
        let err = parse_catalog(reader, ParseOptions::new()).unwrap_err();
        assert_variant!(err, DecodingError);
    }
}
<|endoftext|>


<commit_before>use dungeon::{Dungeon, Level};
use geometry::Point;
use grid;
use tile::Tile;
use item;

pub struct Player {
    pub position: Point,
    pub depth: u8,
    pub name: [u8; 15],
    pub hp: u8,
    pub tp: u8,
    pub xl: u8,
    pub def: i8,

    // Index with element::Element.
    pub aptitude: [i8; 4],

    // Must be represented as bytes!
    pub inventory: [u8; 8],

    pub appearance_byte: u8,
    pub door_appearance_byte: u8,
    pub wall_appearance_byte: u8,

    // Index with byte::BitNumber.
    pub spell_memory: [bool; 8],

    // Index with timer::Timer.
    pub timer: [u8; 4],

    // The address the player's spells will act on.
    pub selected: u8,

    pub stairs_delta: u8,
    pub timer_delta: u8,
    pub damage_offset: i8,
    pub text_sync: u8,

    // Interface
    pub show_ram: bool,

    pub item_appearance_map: item::AppearanceMap,
}

impl Player {
    // enters first level automatically
    pub fn new(dungeon: &Dungeon) -> Player {
        let mut player = Player {
            position: Point(-1, -1),
            depth: 1,
            // TODO naming the player
            name: [97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            hp: 10,
            tp: 3,
            xl: 1,
            def: 0,
            aptitude: [0, 0, 0, 0],
            inventory: [0, 0, 0, 0, 0, 0, 0, 0],
            appearance_byte: 0b11111111, // white @
            door_appearance_byte: 0b11001010, // brown +
            wall_appearance_byte: 0b01100010, // teal #
            spell_memory: [false; 8],
            timer: [0; 4],
            selected: 0x00,
            stairs_delta: 1,
            timer_delta: 0xFF,
            damage_offset: 0,
            text_sync: 0,
            show_ram: false,
            item_appearance_map: item::random_appearance_map(),
        };
        player.enter_level(&dungeon, 1, Tile::StairsUp);
        player
    }

    fn current_level<'a>(&self, dungeon: &'a Dungeon) -> &'a Level {
        &dungeon[self.depth as usize - 1]
    }

    fn current_level_mut<'a>(&self, dungeon: &'a mut Dungeon) -> &'a mut Level {
        &mut dungeon[self.depth as usize - 1]
    }

    // enter a level: put the player on the appropriate stairs.
    // TODO: special case for level 0
    fn enter_level(&mut self, dungeon: &Dungeon, depth: u8, entry: Tile) {
        self.depth = depth;

        for tile_position in grid::RECTANGLE {
            if self.current_level(dungeon).tiles[tile_position] == entry {
                self.position = tile_position;
                break
            }
        }

    }

    // try to walk in given direction
    // TODO: return whether this consumes a turn / requires FOV update or
    // updates from flipping a switch
    pub fn step(&mut self, dungeon: &mut Dungeon, direction: Point) {
        let level = self.current_level_mut(dungeon);
        let new_position = self.position + direction;

        // TODO: check for monsters
        match level.tiles[new_position] {
            Tile::Wall => {},
            Tile::Floor | Tile::Doorway | Tile::StairsUp | Tile::StairsDown => {
                self.position = new_position
            },
            Tile::Door => {
                // TODO: open door
            },
            Tile::Switch(bn) => {
                // TODO: flip switch
            }
        }
    }
}
<commit_msg>let the player open doors<commit_after>use dungeon::{Dungeon, Level};
use geometry::Point;
use grid;
use tile::Tile;
use item;

pub struct Player {
    pub position: Point,
    pub depth: u8,
    pub name: [u8; 15],
    pub hp: u8,
    pub tp: u8,
    pub xl: u8,
    pub def: i8,

    // Index with element::Element.
    pub aptitude: [i8; 4],

    // Must be represented as bytes!
    pub inventory: [u8; 8],

    pub appearance_byte: u8,
    pub door_appearance_byte: u8,
    pub wall_appearance_byte: u8,

    // Index with byte::BitNumber.
    pub spell_memory: [bool; 8],

    // Index with timer::Timer.
    pub timer: [u8; 4],

    // The address the player's spells will act on.
    pub selected: u8,

    pub stairs_delta: u8,
    pub timer_delta: u8,
    pub damage_offset: i8,
    pub text_sync: u8,

    // Interface
    pub show_ram: bool,

    pub item_appearance_map: item::AppearanceMap,
}

impl Player {
    // enters first level automatically
    pub fn new(dungeon: &Dungeon) -> Player {
        let mut player = Player {
            position: Point(-1, -1),
            depth: 1,
            // TODO naming the player
            name: [97, 98, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            hp: 10,
            tp: 3,
            xl: 1,
            def: 0,
            aptitude: [0, 0, 0, 0],
            inventory: [0, 0, 0, 0, 0, 0, 0, 0],
            appearance_byte: 0b11111111, // white @
            door_appearance_byte: 0b11001010, // brown +
            wall_appearance_byte: 0b01100010, // teal #
            spell_memory: [false; 8],
            timer: [0; 4],
            selected: 0x00,
            stairs_delta: 1,
            timer_delta: 0xFF,
            damage_offset: 0,
            text_sync: 0,
            show_ram: false,
            item_appearance_map: item::random_appearance_map(),
        };
        player.enter_level(&dungeon, 1, Tile::StairsUp);
        player
    }

    fn current_level<'a>(&self, dungeon: &'a Dungeon) -> &'a Level {
        &dungeon[self.depth as usize - 1]
    }

    fn current_level_mut<'a>(&self, dungeon: &'a mut Dungeon) -> &'a mut Level {
        &mut dungeon[self.depth as usize - 1]
    }

    // enter a level: put the player on the appropriate stairs.
    // TODO: special case for level 0
    fn enter_level(&mut self, dungeon: &Dungeon, depth: u8, entry: Tile) {
        self.depth = depth;

        for tile_position in grid::RECTANGLE {
            if self.current_level(dungeon).tiles[tile_position] == entry {
                self.position = tile_position;
                break
            }
        }

    }

    // try to walk in given direction
    // TODO: return whether this consumes a turn / requires FOV update or
    // updates from flipping a switch
    pub fn step(&mut self, dungeon: &mut Dungeon, direction: Point) {
        let level = self.current_level_mut(dungeon);
        let new_position = self.position + direction;

        // TODO: check for monsters
        match level.tiles[new_position] {
            Tile::Wall => {},
            Tile::Floor | Tile::Doorway | Tile::StairsUp | Tile::StairsDown => {
                self.position = new_position
            },
            Tile::Door => {
                level.tiles[new_position] = Tile::Doorway
            },
            Tile::Switch(bn) => {
                // TODO: flip switch
            }
        }
    }
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};

use crate::{Range, TextDocumentPositionParams, WorkDoneProgressOptions, WorkDoneProgressParams};

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameParams {
    /// Text Document and Position fields
    #[serde(flatten)]
    pub text_document_position: TextDocumentPositionParams,

    /// The new name of the symbol. If the given name is not valid the
    /// request must return a [ResponseError](#ResponseError) with an
    /// appropriate message set.
    pub new_name: String,

    #[serde(flatten)]
    pub work_done_progress_params: WorkDoneProgressParams,
}

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameOptions {
    /// Renames should be checked and tested before being executed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_provider: Option<bool>,

    #[serde(flatten)]
    pub work_done_progress_options: WorkDoneProgressOptions,
}

#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameClientCapabilities {
    /// Whether rename supports dynamic registration.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dynamic_registration: Option<bool>,

    /// Client supports testing for validity of rename operations before execution.
    ///
    /// since 3.12.0
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_support: Option<bool>,

    /// Client supports the default behavior result (`{ defaultBehavior: boolean }`).
    ///
    /// since 3.16.0
    #[cfg(feature = "proposed")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_support_default_behavior: Option<bool>,

    /// Whether the client honors the change annotations in
    /// text edits and resource operations returned via the
    /// rename request's workspace edit by for example presenting
    /// the workspace edit in the user interface and asking
    /// for confirmation.
    ///
    /// @since 3.16.0 - proposed state
    #[cfg(feature = "proposed")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub honors_change_annotations: Option<bool>,
}

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum PrepareRenameResponse {
    Range(Range),
    RangeWithPlaceholder {
        range: Range,
        placeholder: String,
    },
    #[cfg(feature = "proposed")]
    DefaultBehavior {
        default_behavior: bool,
    },
}
<commit_msg>Change prepare_support_default_behavior to enum<commit_after>use crate::{Range, TextDocumentPositionParams, WorkDoneProgressOptions, WorkDoneProgressParams};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameParams {
    /// Text Document and Position fields
    #[serde(flatten)]
    pub text_document_position: TextDocumentPositionParams,

    /// The new name of the symbol. If the given name is not valid the
    /// request must return a [ResponseError](#ResponseError) with an
    /// appropriate message set.
    pub new_name: String,

    #[serde(flatten)]
    pub work_done_progress_params: WorkDoneProgressParams,
}

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameOptions {
    /// Renames should be checked and tested before being executed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_provider: Option<bool>,

    #[serde(flatten)]
    pub work_done_progress_options: WorkDoneProgressOptions,
}

#[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameClientCapabilities {
    /// Whether rename supports dynamic registration.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dynamic_registration: Option<bool>,

    /// Client supports testing for validity of rename operations before execution.
    ///
    /// since 3.12.0
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_support: Option<bool>,

    /// Client supports the default behavior result.
    ///
    /// The value indicates the default behavior used by the
    /// client.
    ///
    /// since 3.16.0
    #[cfg(feature = "proposed")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prepare_support_default_behavior: Option<PrepareSupportDefaultBehavior>,

    /// Whether the client honors the change annotations in
    /// text edits and resource operations returned via the
    /// rename request's workspace edit by for example presenting
    /// the workspace edit in the user interface and asking
    /// for confirmation.
    ///
    /// @since 3.16.0 - proposed state
    #[cfg(feature = "proposed")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub honors_change_annotations: Option<bool>,
}

#[cfg(feature = "proposed")]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum PrepareSupportDefaultBehavior {
    /// The client's default behavior is to select the identifier
    /// according the to language's syntax rule
    Identifier = 1,
}

#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[serde(rename_all = "camelCase")]
pub enum PrepareRenameResponse {
    Range(Range),
    RangeWithPlaceholder {
        range: Range,
        placeholder: String,
    },
    #[cfg(feature = "proposed")]
    DefaultBehavior {
        default_behavior: bool,
    },
}
<|endoftext|>


<commit_before>use Serializer;

pub trait Router {
    type Response: Response;
    fn attach_index<F: FnOnce(&[String]) -> Self::Response>(&mut self, route: &'static str, f: F);
    fn attach_get<F>(&mut self, route: &'static str, f: F)
        where F: FnOnce(&str, &[String]) -> Self::Response;
    fn attach_put<F: FnOnce(())>(&mut self, route: &'static str, f: F);
    fn attach_patch<F: FnOnce(())>(&mut self, route: &'static str, f: F);
}

pub enum Status {
    Ok = 200,
    BadRequest = 400,
    NotFound = 404,
    InternalError = 500,
}

pub trait Response: Default {
    type Serializer: Serializer;
    fn set_status(&mut self, status: Status);
    fn serializer(&mut self) -> &mut Self::Serializer;
}

<commit_msg>Dead code.<commit_after>use Serializer;

pub trait Router {
    type Response: Response;
    fn attach_index<F: FnOnce(&[String]) -> Self::Response>(&mut self, route: &'static str, f: F);
    fn attach_get<F>(&mut self, route: &'static str, f: F)
        where F: FnOnce(&str, &[String]) -> Self::Response;
}

pub enum Status {
    Ok = 200,
    BadRequest = 400,
    NotFound = 404,
    InternalError = 500,
}

pub trait Response: Default {
    type Serializer: Serializer;
    fn set_status(&mut self, status: Status);
    fn serializer(&mut self) -> &mut Self::Serializer;
}

<|endoftext|>


<commit_before>use Serializer;

pub trait Router {
    type Response: Response;
    fn attach_index<F: FnOnce(&[String]) -> Self::Response>(&mut self, route: &'static str, f: F);
    fn attach_get<F>(&mut self, route: &'static str, f: F)
        where F: FnOnce(&str, &[String]) -> Self::Response;
    fn attach_put<F: FnOnce(())>(&mut self, route: &'static str, f: F);
    fn attach_patch<F: FnOnce(())>(&mut self, route: &'static str, f: F);
}

pub enum Status {
    Ok = 200,
    BadRequest = 400,
    NotFound = 404,
    InternalError = 500,
}

pub trait GetRequest {
    fn id(&self) -> &str;
}

pub trait Response: Default {
    type Serializer: Serializer;
    fn set_status(&mut self, status: Status);
    fn serializer(&mut self) -> &mut Self::Serializer;
}

<commit_msg>Dead code.<commit_after>use Serializer;

pub trait Router {
    type Response: Response;
    fn attach_index<F: FnOnce(&[String]) -> Self::Response>(&mut self, route: &'static str, f: F);
    fn attach_get<F>(&mut self, route: &'static str, f: F)
        where F: FnOnce(&str, &[String]) -> Self::Response;
    fn attach_put<F: FnOnce(())>(&mut self, route: &'static str, f: F);
    fn attach_patch<F: FnOnce(())>(&mut self, route: &'static str, f: F);
}

pub enum Status {
    Ok = 200,
    BadRequest = 400,
    NotFound = 404,
    InternalError = 500,
}

pub trait Response: Default {
    type Serializer: Serializer;
    fn set_status(&mut self, status: Status);
    fn serializer(&mut self) -> &mut Self::Serializer;
}

<|endoftext|>