<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use byteorder::{LE, ReadBytesExt};

use Error;

use std::{fmt, io};

/// Represents a Glb loader error.
#[derive(Debug)]
pub enum GlbError {
    /// Io error occured.
    IoError(::std::io::Error),
    /// Unsupported version.
    Version(u32),
    /// Magic says that file is not glTF.
    Magic([u8; 4]),
    /// Length specified in GLB header exceeeds that of slice.
    Length {
        /// length specified in GLB header.
        length: u32,
        /// Actual length of data read.
        length_read: usize,
    },
    /// Stream ended before we could read the chunk.
    ChunkLength {
        /// chunkType error happened at.
        ty: ChunkType,
        /// chunkLength.
        length: u32,
        /// Actual length of data read.
        length_read: usize,
    },
    /// Chunk of this chunkType was not expected.
    ChunkType(ChunkType),
    /// Unknown chunk type.
    UnknownChunkType([u8; 4]),
}

/// The contents of a .glb file.
#[derive(Clone, Debug)]
pub struct Glb<'a> {
    /// The header section of the `.glb` file.
    pub header: Header,
    /// The JSON section of the `.glb` file.
    pub json: &'a [u8],
    /// The optional BIN section of the `.glb` file.
    pub bin: Option<&'a [u8]>,
}

/// The header section of a .glb file.
#[derive(Copy, Clone, Debug)]
pub struct Header {
    /// Must be `b"glTF"`.
    pub magic: [u8; 4],
    /// Must be `2`.
    pub version: u32,
    /// Must match the length of the parent .glb file.
    pub length: u32,
}

/// GLB chunk type.
#[derive(Copy, Clone, Debug)]
pub enum ChunkType {
    /// JSON chunk.
    Json,
    /// BIN\0 chunk.
    Bin,
}

/// Chunk header with no data read yet.
#[derive(Copy, Clone, Debug)]
struct ChunkHeader {
    /// The length of the chunk data in byte excluding the header.
    length: u32,
    /// Chunk type.
    ty: ChunkType,
}

impl Header {
    fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, GlbError> {
        use self::GlbError::IoError;
        let mut magic = [0; 4];
        reader.read_exact(&mut magic).map_err(IoError)?;
        // We only validate magic as we don't care for version and length of
        // contents, the caller does.  Let them decide what to do next with
        // regard to version and length.
        if &magic == b"glTF" {
            Ok(Self {
                magic,
                version: reader.read_u32::<LE>().map_err(IoError)?,
                length: reader.read_u32::<LE>().map_err(IoError)?,
            })
        } else {
            Err(GlbError::Magic(magic))
        }
    }

    fn size_of() -> usize { 12 }
}

impl ChunkHeader {
    fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, GlbError> {
        use self::GlbError::IoError;
        let length = reader.read_u32::<LE>().map_err(IoError)?;
        let mut ty = [0; 4];
        reader.read_exact(&mut ty).map_err(IoError)?;
        let ty = match &ty {
            b"JSON" => Ok(ChunkType::Json),
            b"BIN\0" => Ok(ChunkType::Bin),
            _ => Err(GlbError::UnknownChunkType(ty)),
        }?;
        Ok(Self { length, ty })
    }
}

impl<'a> Glb<'a> {
    /// Splits loaded GLB into its three chunks.
    ///
    /// * Mandatory GLB header.
    /// * Mandatory JSON chunk.
    /// * Optional BIN chunk.
    pub fn from_slice(mut data: &'a [u8]) -> Result<Self, Error> {
        let header = Header::from_reader(&mut data)
            .and_then(|header| {
                if header.length as usize - Header::size_of() <= data.len() {
                    Ok(header)
                } else {
                    Err(GlbError::Length {
                        length: header.length - Header::size_of(),
                        length_read: data.len(),
                    })
                }
            })
            .map_err(Error::Glb)?;
        match header.version {
            2 => Self::from_v2(data)
                .map(|(json, bin)| Glb { header, json, bin })
                .map_err(Error::Glb),
            x => Err(Error::Glb(GlbError::Version(x)))
        }
    }

    /// Does the loading job for you.  Provided buf will be cleared before new
    /// data will be written.  When error happens, if only header was read, buf
    /// will not be mutated, otherwise, buf will be empty.
    pub fn from_reader<R: io::Read>(mut reader: R,
                                    buf: &'a mut Vec<u8>) -> Result<Self, Error> {
        let header = Header::from_reader(&mut reader).map_err(Error::Glb)?;
        match header.version {
            2 => {
                let glb_len = header.length - Header::size_of() as u32;
                buf.clear();
                buf.reserve(glb_len as usize);
                // SAFETY: We are doing unsafe operation on a user-supplied
                // container!  Make sure not to expose user to uninitialized
                // data if an error happens during reading.
                //
                // It is guaranteed by reserve's implementation that the reserve
                // call will make buf's capacity _at least_ buf_len.
                //
                // We do not read contents of the Vec unless it is fully
                // initialized.
                unsafe { buf.set_len(glb_len as usize) };
                if let Err(e) = reader.read_exact(buf).map_err(GlbError::IoError) {
                    // SAFETY: It is safe to not run destructors because u8 has
                    // none.
                    unsafe { buf.set_len(0) };
                    Err(Error::Glb(e))
                } else {
                    Self::from_v2(buf)
                       .map(|(json, bin)| Glb { header, json, bin })
                       .map_err(Error::Glb)
                }
            }
            x => Err(Error::Glb(GlbError::Version(x)))
        }
    }

    fn from_v2(mut data: &'a [u8]) -> Result<(&'a [u8], Option<&'a [u8]>), GlbError> {
        let (json, mut data) = ChunkHeader::from_reader(&mut data)
            .and_then(|json_h| if let ChunkType::Json = json_h.ty {
                Ok(json_h)
            } else {
                Err(GlbError::ChunkType(json_h.ty))
            })
            .and_then(|json_h| if json_h.length as usize <= data.len() {
                Ok(json_h)
            } else {
                Err(GlbError::ChunkLength {
                    ty: json_h.ty,
                    length: json_h.length,
                    length_read: data.len(),
                })
            })
            // PANIC: We have verified that json_h.length is no greater than
            // that of data.len().
            .map(|json_h| data.split_at(json_h.length as usize))?;

        let bin = if data.len() > 0 {
            ChunkHeader::from_reader(&mut data)
                .and_then(|bin_h| if let ChunkType::Bin = bin_h.ty {
                    Ok(bin_h)
                } else {
                    Err(GlbError::ChunkType(bin_h.ty))
                })
                .and_then(|bin_h| if bin_h.length as usize <= data.len() {
                    Ok(bin_h)
                } else {
                    Err(GlbError::ChunkLength {
                        ty: bin_h.ty,
                        length: bin_h.length,
                        length_read: data.len(),
                    })
                })
                // PANIC: we have verified that bin_h.length is no greater than
                // that of data.len().
                .map(|bin_h| data.split_at(bin_h.length as usize))
                .map(|(x, _)| Some(x))?
        } else {
            None
        };
        Ok((json, bin))
    }
}

impl fmt::Display for GlbError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl ::std::error::Error for GlbError {
    fn description(&self) -> &str {
         match *self {
             GlbError::IoError(ref e) => e.description(),
             GlbError::Version(_) => "unsupported version",
             GlbError::Magic(_) => "not glTF magic",
             GlbError::Length { .. } => "could not completely read the object",
             GlbError::ChunkLength { ty, .. } => match ty {
                 ChunkType::Json => "JSON chunk length exceeds that of slice",
                 ChunkType::Bin => "BIN\\0 chunk length exceeds that of slice",
             },
             GlbError::ChunkType(ty) => match ty {
                 ChunkType::Json => "was not expecting JSON chunk",
                 ChunkType::Bin => "was not expecting BIN\\0 chunk",
             },
             GlbError::UnknownChunkType(_) => "unknown chunk type",
        }
    }
}
<commit_msg>Reduce code duplication<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use byteorder::{LE, ReadBytesExt};

use Error;

use std::{fmt, io};

/// Represents a Glb loader error.
#[derive(Debug)]
pub enum GlbError {
    /// Io error occured.
    IoError(::std::io::Error),
    /// Unsupported version.
    Version(u32),
    /// Magic says that file is not glTF.
    Magic([u8; 4]),
    /// Length specified in GLB header exceeeds that of slice.
    Length {
        /// length specified in GLB header.
        length: u32,
        /// Actual length of data read.
        length_read: usize,
    },
    /// Stream ended before we could read the chunk.
    ChunkLength {
        /// chunkType error happened at.
        ty: ChunkType,
        /// chunkLength.
        length: u32,
        /// Actual length of data read.
        length_read: usize,
    },
    /// Chunk of this chunkType was not expected.
    ChunkType(ChunkType),
    /// Unknown chunk type.
    UnknownChunkType([u8; 4]),
}

/// The contents of a .glb file.
#[derive(Clone, Debug)]
pub struct Glb<'a> {
    /// The header section of the `.glb` file.
    pub header: Header,
    /// The JSON section of the `.glb` file.
    pub json: &'a [u8],
    /// The optional BIN section of the `.glb` file.
    pub bin: Option<&'a [u8]>,
}

/// The header section of a .glb file.
#[derive(Copy, Clone, Debug)]
pub struct Header {
    /// Must be `b"glTF"`.
    pub magic: [u8; 4],
    /// Must be `2`.
    pub version: u32,
    /// Must match the length of the parent .glb file.
    pub length: u32,
}

/// GLB chunk type.
#[derive(Copy, Clone, Debug)]
pub enum ChunkType {
    /// JSON chunk.
    Json,
    /// BIN\0 chunk.
    Bin,
}

/// Chunk header with no data read yet.
#[derive(Copy, Clone, Debug)]
struct ChunkHeader {
    /// The length of the chunk data in byte excluding the header.
    length: u32,
    /// Chunk type.
    ty: ChunkType,
}

impl Header {
    fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, GlbError> {
        use self::GlbError::IoError;
        let mut magic = [0; 4];
        reader.read_exact(&mut magic).map_err(IoError)?;
        // We only validate magic as we don't care for version and length of
        // contents, the caller does.  Let them decide what to do next with
        // regard to version and length.
        if &magic == b"glTF" {
            Ok(Self {
                magic,
                version: reader.read_u32::<LE>().map_err(IoError)?,
                length: reader.read_u32::<LE>().map_err(IoError)?,
            })
        } else {
            Err(GlbError::Magic(magic))
        }
    }

    fn size_of() -> usize { 12 }
}

impl ChunkHeader {
    fn from_reader<R: io::Read>(mut reader: R) -> Result<Self, GlbError> {
        use self::GlbError::IoError;
        let length = reader.read_u32::<LE>().map_err(IoError)?;
        let mut ty = [0; 4];
        reader.read_exact(&mut ty).map_err(IoError)?;
        let ty = match &ty {
            b"JSON" => Ok(ChunkType::Json),
            b"BIN\0" => Ok(ChunkType::Bin),
            _ => Err(GlbError::UnknownChunkType(ty)),
        }?;
        Ok(Self { length, ty })
    }
}

impl<'a> Glb<'a> {
    /// Splits loaded GLB into its three chunks.
    ///
    /// * Mandatory GLB header.
    /// * Mandatory JSON chunk.
    /// * Optional BIN chunk.
    pub fn from_slice(mut data: &'a [u8]) -> Result<Self, Error> {
        let header = Header::from_reader(&mut data)
            .and_then(|header| {
                let contents_length = header.length as usize - Header::size_of();
                if contents_length <= data.len() {
                    Ok(header)
                } else {
                    Err(GlbError::Length {
                        length: contents_length,
                        length_read: data.len(),
                    })
                }
            })
            .map_err(Error::Glb)?;
        match header.version {
            2 => Self::from_v2(data)
                .map(|(json, bin)| Glb { header, json, bin })
                .map_err(Error::Glb),
            x => Err(Error::Glb(GlbError::Version(x)))
        }
    }

    /// Does the loading job for you.  Provided buf will be cleared before new
    /// data will be written.  When error happens, if only header was read, buf
    /// will not be mutated, otherwise, buf will be empty.
    pub fn from_reader<R: io::Read>(mut reader: R,
                                    buf: &'a mut Vec<u8>) -> Result<Self, Error> {
        let header = Header::from_reader(&mut reader).map_err(Error::Glb)?;
        match header.version {
            2 => {
                let glb_len = header.length - Header::size_of() as u32;
                buf.clear();
                buf.reserve(glb_len as usize);
                // SAFETY: We are doing unsafe operation on a user-supplied
                // container!  Make sure not to expose user to uninitialized
                // data if an error happens during reading.
                //
                // It is guaranteed by reserve's implementation that the reserve
                // call will make buf's capacity _at least_ buf_len.
                //
                // We do not read contents of the Vec unless it is fully
                // initialized.
                unsafe { buf.set_len(glb_len as usize) };
                if let Err(e) = reader.read_exact(buf).map_err(GlbError::IoError) {
                    // SAFETY: It is safe to not run destructors because u8 has
                    // none.
                    unsafe { buf.set_len(0) };
                    Err(Error::Glb(e))
                } else {
                    Self::from_v2(buf)
                       .map(|(json, bin)| Glb { header, json, bin })
                       .map_err(Error::Glb)
                }
            }
            x => Err(Error::Glb(GlbError::Version(x)))
        }
    }

    fn from_v2(mut data: &'a [u8]) -> Result<(&'a [u8], Option<&'a [u8]>), GlbError> {
        let (json, mut data) = ChunkHeader::from_reader(&mut data)
            .and_then(|json_h| if let ChunkType::Json = json_h.ty {
                Ok(json_h)
            } else {
                Err(GlbError::ChunkType(json_h.ty))
            })
            .and_then(|json_h| if json_h.length as usize <= data.len() {
                Ok(json_h)
            } else {
                Err(GlbError::ChunkLength {
                    ty: json_h.ty,
                    length: json_h.length,
                    length_read: data.len(),
                })
            })
            // PANIC: We have verified that json_h.length is no greater than
            // that of data.len().
            .map(|json_h| data.split_at(json_h.length as usize))?;

        let bin = if data.len() > 0 {
            ChunkHeader::from_reader(&mut data)
                .and_then(|bin_h| if let ChunkType::Bin = bin_h.ty {
                    Ok(bin_h)
                } else {
                    Err(GlbError::ChunkType(bin_h.ty))
                })
                .and_then(|bin_h| if bin_h.length as usize <= data.len() {
                    Ok(bin_h)
                } else {
                    Err(GlbError::ChunkLength {
                        ty: bin_h.ty,
                        length: bin_h.length,
                        length_read: data.len(),
                    })
                })
                // PANIC: we have verified that bin_h.length is no greater than
                // that of data.len().
                .map(|bin_h| data.split_at(bin_h.length as usize))
                .map(|(x, _)| Some(x))?
        } else {
            None
        };
        Ok((json, bin))
    }
}

impl fmt::Display for GlbError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

impl ::std::error::Error for GlbError {
    fn description(&self) -> &str {
         match *self {
             GlbError::IoError(ref e) => e.description(),
             GlbError::Version(_) => "unsupported version",
             GlbError::Magic(_) => "not glTF magic",
             GlbError::Length { .. } => "could not completely read the object",
             GlbError::ChunkLength { ty, .. } => match ty {
                 ChunkType::Json => "JSON chunk length exceeds that of slice",
                 ChunkType::Bin => "BIN\\0 chunk length exceeds that of slice",
             },
             GlbError::ChunkType(ty) => match ty {
                 ChunkType::Json => "was not expecting JSON chunk",
                 ChunkType::Bin => "was not expecting BIN\\0 chunk",
             },
             GlbError::UnknownChunkType(_) => "unknown chunk type",
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! This module contains the `Cleaner` traits and various implementations of it.

use std::borrow::Cow;

/// Custom function because we don't really want to touch \t or \n
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == ' ' || c == ' '
}

/// Trait for cleaning a string.
///
/// This trait must be called for text that is e.g. in a paragraph, a title,
/// NOT for code blocks, hyperlinks and so on!
pub trait Cleaner: Sync {
    /// Cleans a string. The default implementation is to remove multiple consecutive whitespaces
    ///
    /// # Argumets
    ///
    /// * `str`: the string that must be cleaned
    /// * `latex`: a bool specifying whether output is Latex code or not
    fn clean<'a>(&self, str: Cow<'a, str>, _latex: bool) -> Cow<'a, str> {
        str
    }
}

/// Cleaner implementation that does nothing
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Off;
/// use std::borrow::Cow;
/// let off = Off;
/// let s = off.clean(Cow::Borrowed("  A string   that won't be cleaned "), false);
/// assert_eq!(&s, "  A string   that won't be cleaned ");
/// ```
pub struct Off;
impl Cleaner for Off {}

/// Default implementation of cleaner trait.
///
/// Only removes unnecessary whitespaces.
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Default;
/// use std::borrow::Cow;
/// let s = Default.clean(Cow::Borrowed("  A  string   with   more   whitespaces  than  needed   "), false);
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub struct Default;
impl Cleaner for Default {
    /// Remove unnecessary whitespaces
    fn clean<'a>(&self, s: Cow<'a, str>, _: bool) -> Cow<'a, str> {
        if s.contains(is_whitespace) { // if not, no need to do anything
            let mut new_s = String::with_capacity(s.len());
            let mut previous_space = false;
            for c in s.chars() {
                if is_whitespace(c) {
                    if previous_space {
                        // previous char already a space, don't copy it
                    } else {
                        new_s.push(c);
                        previous_space = true;
                    }
                } else {
                    previous_space = false;
                    new_s.push(c);
                }
            }

            Cow::Owned(new_s)
        } else {
            s
        }
    }
}

/// Implementation for french 'cleaning'
///
/// This implementation replaces spaces before some characters (e.g. `?` or `;` with non-breaking spaces
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::French;
/// use std::borrow::Cow;
/// let s =  French.clean(Cow::Borrowed("  Bonjour ! Comment allez-vous   ?   "), true);
/// assert_eq!(&s, " Bonjour~! Comment allez-vous~? ");
/// ```
pub struct French;

const THRESHOLD_CURRENCY: usize = 3; // after that, assume it's not a currency
const THRESHOLD_UNIT: usize = 2; // after that, assume it's not a unit
const THRESHOLD_QUOTE: usize = 28; // after that, assume it's a dialogue
const THRESHOLD_REAL_WORD: usize = 3; // after that, can be reasonably sure it is not an abbreviation

impl Cleaner for French {
    /// Puts non breaking spaces before/after `:`, `;`, `?`, `!`, `«`, `»`, `—`
    fn clean<'a>(&self, s: Cow<'a, str>, latex: bool) -> Cow<'a, str> {
        fn is_trouble(c: char) -> bool {
            match c {
                '?'|'!'|';'|':'|'»'|'«'|'—'|'–' | '0'...'9' => true,
                _ => false
            }
        }
        if !s.contains(is_trouble) { // if not, no need to do anything
            return Default.clean(s, latex);
        }
        let nb_char = if latex {
            '~'
        } else {
            ' ' // non breaking space
        };
        let nb_char_narrow = if latex {
            '~'
        } else {
            '\u{202F}' // narrow non breaking space
        };
        let nb_char_em = if latex {
            '~'
        } else {
            '\u{2002}' // demi em space
        };

        // Find first char `c` in slice `v` after index `n`
        fn find_next(v: &[char], c: char, n: usize) -> Option<usize> {
            for i in n..v.len() {
                if v[i] == c  {
                    return Some(i);
                }
            }
            None
        }

        // Return true if next non whitespace char in `v` after index `n` is uppercase
        fn is_next_char_uppercase(v: &[char], n: usize)-> bool {
            for i in n..v.len() {
                if v[i].is_whitespace() {
                    continue;
                }
                if v[i].is_uppercase() {
                    return true;
                }
                if v[i].is_lowercase() {
                    return false;
                }
            }
            false
        }

        // Return true(some) if a closing dash was found before what looks like the end of a sentence, None else
        fn find_closing_dash(v: &[char], n: usize) -> Option<usize> {
            let mut word = String::new();
            for j in n..v.len() {
                match v[j] {

                    '!' | '?' => if is_next_char_uppercase(v, j+1) {
                        return None;
                    },
                    '-' | '–' | '—' => if v[j-1].is_whitespace() {
                        return Some(j-1);
                    },
                    '.' => if !is_next_char_uppercase(v, j+1) {
                        continue;
                    } else {
                        if let Some(c) = word.chars().next() {
                            if !c.is_uppercase() {
                                return None;
                            } else {
                                if word.len() > THRESHOLD_REAL_WORD {
                                    return None;
                                }
                            }
                        }
                    },
                    c if c.is_whitespace() => word = String::new(),
                    c => word.push(c),
                }
            }
            return None;
        }

        /// Returns the next word in `v` starting from index `n`
        fn get_next_word(v: &[char], n: usize) -> &[char] {
            let mut beginning = n;
            let mut end = v.len();

            for i in n..v.len() {
                if v[i].is_alphabetic() {
                    beginning = i;
                    break;
                }
            }

            for i in beginning..v.len() {
                if v[i].is_whitespace() {
                    end = i;
                    break;
                }
            }

            &v[beginning..end]
        }

        /// Return true if the character is a symbol that is used after number and should have a nb_char before
        fn char_is_symbol(v: &[char], i: usize) -> bool {
            let is_next_letter = if i < v.len() - 1 {
                v[i+1].is_alphabetic()
            } else {
                false
            };
            if is_next_letter {
                match v[i] {
                    '°' => true,
                    c if c.is_uppercase() => {
                        let word = get_next_word(v, i);
                        if word.len() > THRESHOLD_CURRENCY {
                            // not a currency
                            false
                        } else {
                            // if all uppercase and less than THRESHOLD, assume it's a currency or a unit
                            word.iter().all(|c| c.is_uppercase())
                        }
                    },
                    c if c.is_alphabetic() => {
                        let word = get_next_word(v, i);
                        // if two letters, assume it is a unit
                        word.len() <= THRESHOLD_UNIT
                    },
                    _ => false
                }
            } else {
                match v[i] {
                    c if (!c.is_alphabetic() && !c.is_whitespace()) => true, // special symbol
                    c if c.is_uppercase() => true, //single uppercase letter
                    _ => false,
                }
            }
        }

        let s = Default.clean(s, latex); // first pass with default impl
        let mut found_opening_quote = false; // we didn't find an opening quote yet
        let mut chars = s.chars().collect::<Vec<_>>();
        let mut is_number_series = false;

        for i in 0..(chars.len()-1) {
            // Handle numbers (that's easy)
            let current = chars[i];
            let next = chars[i+1];

            match current {
                '0'...'9' => if i == 0 {
                    is_number_series = true;
                } else if !chars[i-1].is_alphabetic() {
                    is_number_series = true;
                },
                c if c.is_whitespace() => {
                    if is_number_series && (next.is_digit(10) || char_is_symbol(&chars, i+1)) {
                        // Next char is a number or symbol such as $, and previous was number
                        chars[i] = nb_char_narrow;
                    }
                },
                _ => { is_number_series = false; }
            }
        }

        for i in 0..(chars.len()-1) {
            let current = chars[i];
            let next = chars[i+1];

            // Handle the rest (that's hard)
            if is_whitespace(current) {
                match next {
                    // handle narrow nb space before char
                    '?' | '!' | ';' => chars[i] = nb_char_narrow,
                    ':' => chars[i] = nb_char,
                    '»' => if current == ' ' {
                        // Assumne that if it isn't a normal space it was used here for good reason, don't replace it
                        if found_opening_quote {
                            // not the end of a dialogue
                            chars[i] = nb_char;
                        } else {
                            chars[i] = nb_char;
                        }
                    },
                    _ => (),
                }
            } else {
                match current {
                    // handle nb space after char
                    '—' | '«' | '-' | '–' => {
                        if is_whitespace(next) {
                            let replacing_char = match current {
                                '—' | '-' | '–' => {
                                    if i <= 1 {
                                        nb_char_em
                                    } else {
                                        if chars[i-1] == nb_char {
                                            // non breaking space before, so probably should have a breakable one after
                                            ' '
                                        } else {
                                            if let Some(closing) = find_closing_dash(&chars, i+1) {
                                                chars[closing] = nb_char;
                                            }
                                            nb_char
                                        }
                                    }
                                },
                                '«' => {
                                    found_opening_quote = true;
                                    if i <= 1 {
                                        nb_char
                                    } else {
                                        let j = find_next(&chars, '»', i);
                                        if let Some(j) = j {
                                            if chars[j-1].is_whitespace() {
                                                if j >= chars.len() - 1 {
                                                    // » is at the end, assume it is a dialogue
                                                    chars[j-1] = nb_char;
                                                    nb_char
                                                } else {
                                                    if j - i > THRESHOLD_QUOTE {
                                                        // It's a quote, so use large space?
                                                        chars[j-1] = nb_char;
                                                        nb_char
                                                    } else {
                                                        // Not long enough to be a quote, use narrow nb char
                                                        chars[j-1] = nb_char_narrow;
                                                        nb_char_narrow
                                                    }
                                                }
                                            } else {
                                                // wtf formatting?
                                                nb_char
                                            }
                                        } else {
                                            // No ending quote found, assume is a dialogue
                                            nb_char
                                        }
                                    }
                                }, // TODO: better heuristic: use narrow nb_char if not at front???
                                _ => unreachable!(),
                            };
                            chars[i+1] = replacing_char;
                        }
                    }
                    _ => (),
                }
            }
        }
        Cow::Owned(chars.into_iter().collect())
    }
}

<commit_msg>Fix bug in number spaces before units<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//! This module contains the `Cleaner` traits and various implementations of it.

use std::borrow::Cow;

/// Custom function because we don't really want to touch \t or \n
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == ' ' || c == ' '
}

/// Trait for cleaning a string.
///
/// This trait must be called for text that is e.g. in a paragraph, a title,
/// NOT for code blocks, hyperlinks and so on!
pub trait Cleaner: Sync {
    /// Cleans a string. The default implementation is to remove multiple consecutive whitespaces
    ///
    /// # Argumets
    ///
    /// * `str`: the string that must be cleaned
    /// * `latex`: a bool specifying whether output is Latex code or not
    fn clean<'a>(&self, str: Cow<'a, str>, _latex: bool) -> Cow<'a, str> {
        str
    }
}

/// Cleaner implementation that does nothing
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Off;
/// use std::borrow::Cow;
/// let off = Off;
/// let s = off.clean(Cow::Borrowed("  A string   that won't be cleaned "), false);
/// assert_eq!(&s, "  A string   that won't be cleaned ");
/// ```
pub struct Off;
impl Cleaner for Off {}

/// Default implementation of cleaner trait.
///
/// Only removes unnecessary whitespaces.
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::Default;
/// use std::borrow::Cow;
/// let s = Default.clean(Cow::Borrowed("  A  string   with   more   whitespaces  than  needed   "), false);
/// assert_eq!(&s, " A string with more whitespaces than needed ");
/// ```
pub struct Default;
impl Cleaner for Default {
    /// Remove unnecessary whitespaces
    fn clean<'a>(&self, s: Cow<'a, str>, _: bool) -> Cow<'a, str> {
        if s.contains(is_whitespace) { // if not, no need to do anything
            let mut new_s = String::with_capacity(s.len());
            let mut previous_space = false;
            for c in s.chars() {
                if is_whitespace(c) {
                    if previous_space {
                        // previous char already a space, don't copy it
                    } else {
                        new_s.push(c);
                        previous_space = true;
                    }
                } else {
                    previous_space = false;
                    new_s.push(c);
                }
            }

            Cow::Owned(new_s)
        } else {
            s
        }
    }
}

/// Implementation for french 'cleaning'
///
/// This implementation replaces spaces before some characters (e.g. `?` or `;` with non-breaking spaces
///
/// # Examples
///
/// ```
/// use crowbook::cleaner::Cleaner;
/// use crowbook::cleaner::French;
/// use std::borrow::Cow;
/// let s =  French.clean(Cow::Borrowed("  Bonjour ! Comment allez-vous   ?   "), true);
/// assert_eq!(&s, " Bonjour~! Comment allez-vous~? ");
/// ```
pub struct French;

const THRESHOLD_CURRENCY: usize = 3; // after that, assume it's not a currency
const THRESHOLD_UNIT: usize = 2; // after that, assume it's not a unit
const THRESHOLD_QUOTE: usize = 28; // after that, assume it's a dialogue
const THRESHOLD_REAL_WORD: usize = 3; // after that, can be reasonably sure it is not an abbreviation

impl Cleaner for French {
    /// Puts non breaking spaces before/after `:`, `;`, `?`, `!`, `«`, `»`, `—`
    fn clean<'a>(&self, s: Cow<'a, str>, latex: bool) -> Cow<'a, str> {
        fn is_trouble(c: char) -> bool {
            match c {
                '?'|'!'|';'|':'|'»'|'«'|'—'|'–' | '0'...'9' => true,
                _ => false
            }
        }
        if !s.contains(is_trouble) { // if not, no need to do anything
            return Default.clean(s, latex);
        }
        let nb_char = if latex {
            '~'
        } else {
            ' ' // non breaking space
        };
        let nb_char_narrow = if latex {
            '~'
        } else {
            '\u{202F}' // narrow non breaking space
        };
        let nb_char_em = if latex {
            '~'
        } else {
            '\u{2002}' // demi em space
        };

        // Find first char `c` in slice `v` after index `n`
        fn find_next(v: &[char], c: char, n: usize) -> Option<usize> {
            for i in n..v.len() {
                if v[i] == c  {
                    return Some(i);
                }
            }
            None
        }

        // Return true if next non whitespace char in `v` after index `n` is uppercase
        fn is_next_char_uppercase(v: &[char], n: usize)-> bool {
            for i in n..v.len() {
                if v[i].is_whitespace() {
                    continue;
                }
                if v[i].is_uppercase() {
                    return true;
                }
                if v[i].is_lowercase() {
                    return false;
                }
            }
            false
        }

        // Return true(some) if a closing dash was found before what looks like the end of a sentence, None else
        fn find_closing_dash(v: &[char], n: usize) -> Option<usize> {
            let mut word = String::new();
            for j in n..v.len() {
                match v[j] {

                    '!' | '?' => if is_next_char_uppercase(v, j+1) {
                        return None;
                    },
                    '-' | '–' | '—' => if v[j-1].is_whitespace() {
                        return Some(j-1);
                    },
                    '.' => if !is_next_char_uppercase(v, j+1) {
                        continue;
                    } else {
                        if let Some(c) = word.chars().next() {
                            if !c.is_uppercase() {
                                return None;
                            } else {
                                if word.len() > THRESHOLD_REAL_WORD {
                                    return None;
                                }
                            }
                        }
                    },
                    c if c.is_whitespace() => word = String::new(),
                    c => word.push(c),
                }
            }
            return None;
        }

        /// Returns the next word in `v` starting from index `n`
        fn get_next_word(v: &[char], n: usize) -> &[char] {
            let mut beginning = n;
            let mut end = v.len();

            for i in n..v.len() {
                if v[i].is_alphabetic() {
                    beginning = i;
                    break;
                }
            }

            for i in beginning..v.len() {
                if v[i].is_whitespace() {
                    end = i-1;
                    break;
                }
            }

            &v[beginning..end]
        }

        /// Return true if the character is a symbol that is used after number and should have a nb_char before
        fn char_is_symbol(v: &[char], i: usize) -> bool {
            let is_next_letter = if i < v.len() - 1 {
                v[i+1].is_alphabetic()
            } else {
                false
            };
            if is_next_letter {
                match v[i] {
                    '°' => true,
                    c if c.is_uppercase() => {
                        let word = get_next_word(v, i);
                        if word.len() > THRESHOLD_CURRENCY {
                            // not a currency
                            false
                        } else {
                            // if all uppercase and less than THRESHOLD, assume it's a currency or a unit
                            word.iter().all(|c| c.is_uppercase())
                        }
                    },
                    c if c.is_alphabetic() => {
                        let word = get_next_word(v, i);
                        // if two letters, assume it is a unit
                        word.len() <= THRESHOLD_UNIT
                    },
                    _ => false
                }
            } else {
                match v[i] {
                    c if (!c.is_alphabetic() && !c.is_whitespace()) => true, // special symbol
                    c if c.is_uppercase() => true, //single uppercase letter
                    _ => false,
                }
            }
        }

        let s = Default.clean(s, latex); // first pass with default impl
        let mut found_opening_quote = false; // we didn't find an opening quote yet
        let mut chars = s.chars().collect::<Vec<_>>();
        let mut is_number_series = false;

        for i in 0..(chars.len()-1) {
            // Handle numbers (that's easy)
            let current = chars[i];
            let next = chars[i+1];

            match current {
                '0'...'9' => if i == 0 {
                    is_number_series = true;
                } else if !chars[i-1].is_alphabetic() {
                    is_number_series = true;
                },
                c if c.is_whitespace() => {
                    if is_number_series && (next.is_digit(10) || char_is_symbol(&chars, i+1)) {
                        // Next char is a number or symbol such as $, and previous was number
                        chars[i] = nb_char_narrow;
                    }
                },
                _ => { is_number_series = false; }
            }
        }

        for i in 0..(chars.len()-1) {
            let current = chars[i];
            let next = chars[i+1];

            // Handle the rest (that's hard)
            if is_whitespace(current) {
                match next {
                    // handle narrow nb space before char
                    '?' | '!' | ';' => chars[i] = nb_char_narrow,
                    ':' => chars[i] = nb_char,
                    '»' => if current == ' ' {
                        // Assumne that if it isn't a normal space it was used here for good reason, don't replace it
                        if found_opening_quote {
                            // not the end of a dialogue
                            chars[i] = nb_char;
                        } else {
                            chars[i] = nb_char;
                        }
                    },
                    _ => (),
                }
            } else {
                match current {
                    // handle nb space after char
                    '—' | '«' | '-' | '–' => {
                        if is_whitespace(next) {
                            let replacing_char = match current {
                                '—' | '-' | '–' => {
                                    if i <= 1 {
                                        nb_char_em
                                    } else {
                                        if chars[i-1] == nb_char {
                                            // non breaking space before, so probably should have a breakable one after
                                            ' '
                                        } else {
                                            if let Some(closing) = find_closing_dash(&chars, i+1) {
                                                chars[closing] = nb_char;
                                            }
                                            nb_char
                                        }
                                    }
                                },
                                '«' => {
                                    found_opening_quote = true;
                                    if i <= 1 {
                                        nb_char
                                    } else {
                                        let j = find_next(&chars, '»', i);
                                        if let Some(j) = j {
                                            if chars[j-1].is_whitespace() {
                                                if j >= chars.len() - 1 {
                                                    // » is at the end, assume it is a dialogue
                                                    chars[j-1] = nb_char;
                                                    nb_char
                                                } else {
                                                    if j - i > THRESHOLD_QUOTE {
                                                        // It's a quote, so use large space?
                                                        chars[j-1] = nb_char;
                                                        nb_char
                                                    } else {
                                                        // Not long enough to be a quote, use narrow nb char
                                                        chars[j-1] = nb_char_narrow;
                                                        nb_char_narrow
                                                    }
                                                }
                                            } else {
                                                // wtf formatting?
                                                nb_char
                                            }
                                        } else {
                                            // No ending quote found, assume is a dialogue
                                            nb_char
                                        }
                                    }
                                }, // TODO: better heuristic: use narrow nb_char if not at front???
                                _ => unreachable!(),
                            };
                            chars[i+1] = replacing_char;
                        }
                    }
                    _ => (),
                }
            }
        }
        Cow::Owned(chars.into_iter().collect())
    }
}

<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module implements the `Any` trait, which enables dynamic typing
//! of any `'static` type through runtime reflection.
//!
//! `Any` itself can be used to get a `TypeId`, and has more features when used
//! as a trait object. As `&Any` (a borrowed trait object), it has the `is` and
//! `downcast_ref` methods, to test if the contained value is of a given type,
//! and to get a reference to the inner value as a type. As `&mut Any`, there
//! is also the `downcast_mut` method, for getting a mutable reference to the
//! inner value. `Box<Any>` adds the `downcast` method, which attempts to
//! convert to a `Box<T>`. See the [`Box`] documentation for the full details.
//!
//! Note that &Any is limited to testing whether a value is of a specified
//! concrete type, and cannot be used to test whether a type implements a trait.
//!
//! [`Box`]: ../../std/boxed/struct.Box.html
//!
//! # Examples
//!
//! Consider a situation where we want to log out a value passed to a function.
//! We know the value we're working on implements Debug, but we don't know its
//! concrete type.  We want to give special treatment to certain types: in this
//! case printing out the length of String values prior to their value.
//! We don't know the concrete type of our value at compile time, so we need to
//! use runtime reflection instead.
//!
//! ```rust
//! use std::fmt::Debug;
//! use std::any::Any;
//!
//! // Logger function for any type that implements Debug.
//! fn log<T: Any + Debug>(value: &T) {
//!     let value_any = value as &Any;
//!
//!     // try to convert our value to a String.  If successful, we want to
//!     // output the String's length as well as its value.  If not, it's a
//!     // different type: just print it out unadorned.
//!     match value_any.downcast_ref::<String>() {
//!         Some(as_string) => {
//!             println!("String ({}): {}", as_string.len(), as_string);
//!         }
//!         None => {
//!             println!("{:?}", value);
//!         }
//!     }
//! }
//!
//! // This function wants to log its parameter out prior to doing work with it.
//! fn do_work<T: Any + Debug>(value: &T) {
//!     log(value);
//!     // ...do some other work
//! }
//!
//! fn main() {
//!     let my_string = "Hello World".to_string();
//!     do_work(&my_string);
//!
//!     let my_i8: i8 = 100;
//!     do_work(&my_i8);
//! }
//! ```

#![stable(feature = "rust1", since = "1.0.0")]

use fmt;
use intrinsics;

///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////

/// A type to emulate dynamic typing.
///
/// Most types implement `Any`. However, any type which contains a non-`'static` reference does not.
/// See the [module-level documentation][mod] for more details.
///
/// [mod]: index.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Any: 'static {
    /// Gets the `TypeId` of `self`.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(get_type_id)]
    ///
    /// use std::any::{Any, TypeId};
    ///
    /// fn is_string(s: &Any) -> bool {
    ///     TypeId::of::<String>() == s.get_type_id()
    /// }
    ///
    /// fn main() {
    ///     assert_eq!(is_string(&0), false);
    ///     assert_eq!(is_string(&"cookie monster".to_owned()), true);
    /// }
    /// ```
    #[unstable(feature = "get_type_id",
               reason = "this method will likely be replaced by an associated static",
               issue = "27745")]
    fn get_type_id(&self) -> TypeId;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized > Any for T {
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

///////////////////////////////////////////////////////////////////////////////
// Extension methods for Any trait objects.
///////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for Any {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Any")
    }
}

// Ensure that the result of e.g. joining a thread can be printed and
// hence used with `unwrap`. May eventually no longer be needed if
// dispatch works with upcasting.
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for Any + Send {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Any")
    }
}

impl Any {
    /// Returns true if the boxed type is the same as `T`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn is_string(s: &Any) {
    ///     if s.is::<String>() {
    ///         println!("It's a string!");
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     is_string(&0);
    ///     is_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        // Get TypeId of the type this function is instantiated with
        let t = TypeId::of::<T>();

        // Get TypeId of the type in the trait object
        let boxed = self.get_type_id();

        // Compare both TypeIds on equality
        t == boxed
    }

    /// Returns some reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn print_if_string(s: &Any) {
    ///     if let Some(string) = s.downcast_ref::<String>() {
    ///         println!("It's a string({}): '{}'", string.len(), string);
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     print_if_string(&0);
    ///     print_if_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if self.is::<T>() {
            unsafe {
                Some(&*(self as *const Any as *const T))
            }
        } else {
            None
        }
    }

    /// Returns some mutable reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn modify_if_u32(s: &mut Any) {
    ///     if let Some(num) = s.downcast_mut::<u32>() {
    ///         *num = 42;
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut x = 10u32;
    ///     let mut s = "starlord".to_owned();
    ///
    ///     modify_if_u32(&mut x);
    ///     modify_if_u32(&mut s);
    ///
    ///     assert_eq!(x, 42);
    ///     assert_eq!(&s, "starlord");
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            unsafe {
                Some(&mut *(self as *mut Any as *mut T))
            }
        } else {
            None
        }
    }
}

impl Any+Send {
    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn is_string(s: &(Any + Send)) {
    ///     if s.is::<String>() {
    ///         println!("It's a string!");
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     is_string(&0);
    ///     is_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        Any::is::<T>(self)
    }

    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn print_if_string(s: &(Any + Send)) {
    ///     if let Some(string) = s.downcast_ref::<String>() {
    ///         println!("It's a string({}): '{}'", string.len(), string);
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     print_if_string(&0);
    ///     print_if_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        Any::downcast_ref::<T>(self)
    }

    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn modify_if_u32(s: &mut (Any+ Send)) {
    ///     if let Some(num) = s.downcast_mut::<u32>() {
    ///         *num = 42;
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut x = 10u32;
    ///     let mut s = "starlord".to_owned();
    ///
    ///     modify_if_u32(&mut x);
    ///     modify_if_u32(&mut s);
    ///
    ///     assert_eq!(x, 42);
    ///     assert_eq!(&s, "starlord");
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        Any::downcast_mut::<T>(self)
    }
}

///////////////////////////////////////////////////////////////////////////////
// TypeID and its methods
///////////////////////////////////////////////////////////////////////////////

/// A `TypeId` represents a globally unique identifier for a type.
///
/// Each `TypeId` is an opaque object which does not allow inspection of what's
/// inside but does allow basic operations such as cloning, comparison,
/// printing, and showing.
///
/// A `TypeId` is currently only available for types which ascribe to `'static`,
/// but this limitation may be removed in the future.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct TypeId {
    t: u64,
}

impl TypeId {
    /// Returns the `TypeId` of the type this generic function has been
    /// instantiated with.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::{Any, TypeId};
    ///
    /// fn is_string<T: ?Sized + Any>(_s: &T) -> bool {
    ///     TypeId::of::<String>() == TypeId::of::<T>()
    /// }
    ///
    /// fn main() {
    ///     assert_eq!(is_string(&0), false);
    ///     assert_eq!(is_string(&"cookie monster".to_owned()), true);
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn of<T: ?Sized + 'static>() -> TypeId {
        TypeId {
            t: unsafe { intrinsics::type_id::<T>() },
        }
    }
}
<commit_msg>Add warning about varying hashes/orderings<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module implements the `Any` trait, which enables dynamic typing
//! of any `'static` type through runtime reflection.
//!
//! `Any` itself can be used to get a `TypeId`, and has more features when used
//! as a trait object. As `&Any` (a borrowed trait object), it has the `is` and
//! `downcast_ref` methods, to test if the contained value is of a given type,
//! and to get a reference to the inner value as a type. As `&mut Any`, there
//! is also the `downcast_mut` method, for getting a mutable reference to the
//! inner value. `Box<Any>` adds the `downcast` method, which attempts to
//! convert to a `Box<T>`. See the [`Box`] documentation for the full details.
//!
//! Note that &Any is limited to testing whether a value is of a specified
//! concrete type, and cannot be used to test whether a type implements a trait.
//!
//! [`Box`]: ../../std/boxed/struct.Box.html
//!
//! # Examples
//!
//! Consider a situation where we want to log out a value passed to a function.
//! We know the value we're working on implements Debug, but we don't know its
//! concrete type.  We want to give special treatment to certain types: in this
//! case printing out the length of String values prior to their value.
//! We don't know the concrete type of our value at compile time, so we need to
//! use runtime reflection instead.
//!
//! ```rust
//! use std::fmt::Debug;
//! use std::any::Any;
//!
//! // Logger function for any type that implements Debug.
//! fn log<T: Any + Debug>(value: &T) {
//!     let value_any = value as &Any;
//!
//!     // try to convert our value to a String.  If successful, we want to
//!     // output the String's length as well as its value.  If not, it's a
//!     // different type: just print it out unadorned.
//!     match value_any.downcast_ref::<String>() {
//!         Some(as_string) => {
//!             println!("String ({}): {}", as_string.len(), as_string);
//!         }
//!         None => {
//!             println!("{:?}", value);
//!         }
//!     }
//! }
//!
//! // This function wants to log its parameter out prior to doing work with it.
//! fn do_work<T: Any + Debug>(value: &T) {
//!     log(value);
//!     // ...do some other work
//! }
//!
//! fn main() {
//!     let my_string = "Hello World".to_string();
//!     do_work(&my_string);
//!
//!     let my_i8: i8 = 100;
//!     do_work(&my_i8);
//! }
//! ```

#![stable(feature = "rust1", since = "1.0.0")]

use fmt;
use intrinsics;

///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////

/// A type to emulate dynamic typing.
///
/// Most types implement `Any`. However, any type which contains a non-`'static` reference does not.
/// See the [module-level documentation][mod] for more details.
///
/// [mod]: index.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Any: 'static {
    /// Gets the `TypeId` of `self`.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(get_type_id)]
    ///
    /// use std::any::{Any, TypeId};
    ///
    /// fn is_string(s: &Any) -> bool {
    ///     TypeId::of::<String>() == s.get_type_id()
    /// }
    ///
    /// fn main() {
    ///     assert_eq!(is_string(&0), false);
    ///     assert_eq!(is_string(&"cookie monster".to_owned()), true);
    /// }
    /// ```
    #[unstable(feature = "get_type_id",
               reason = "this method will likely be replaced by an associated static",
               issue = "27745")]
    fn get_type_id(&self) -> TypeId;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized > Any for T {
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

///////////////////////////////////////////////////////////////////////////////
// Extension methods for Any trait objects.
///////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for Any {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Any")
    }
}

// Ensure that the result of e.g. joining a thread can be printed and
// hence used with `unwrap`. May eventually no longer be needed if
// dispatch works with upcasting.
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for Any + Send {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Any")
    }
}

impl Any {
    /// Returns true if the boxed type is the same as `T`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn is_string(s: &Any) {
    ///     if s.is::<String>() {
    ///         println!("It's a string!");
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     is_string(&0);
    ///     is_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        // Get TypeId of the type this function is instantiated with
        let t = TypeId::of::<T>();

        // Get TypeId of the type in the trait object
        let boxed = self.get_type_id();

        // Compare both TypeIds on equality
        t == boxed
    }

    /// Returns some reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn print_if_string(s: &Any) {
    ///     if let Some(string) = s.downcast_ref::<String>() {
    ///         println!("It's a string({}): '{}'", string.len(), string);
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     print_if_string(&0);
    ///     print_if_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if self.is::<T>() {
            unsafe {
                Some(&*(self as *const Any as *const T))
            }
        } else {
            None
        }
    }

    /// Returns some mutable reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn modify_if_u32(s: &mut Any) {
    ///     if let Some(num) = s.downcast_mut::<u32>() {
    ///         *num = 42;
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut x = 10u32;
    ///     let mut s = "starlord".to_owned();
    ///
    ///     modify_if_u32(&mut x);
    ///     modify_if_u32(&mut s);
    ///
    ///     assert_eq!(x, 42);
    ///     assert_eq!(&s, "starlord");
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        if self.is::<T>() {
            unsafe {
                Some(&mut *(self as *mut Any as *mut T))
            }
        } else {
            None
        }
    }
}

impl Any+Send {
    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn is_string(s: &(Any + Send)) {
    ///     if s.is::<String>() {
    ///         println!("It's a string!");
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     is_string(&0);
    ///     is_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn is<T: Any>(&self) -> bool {
        Any::is::<T>(self)
    }

    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn print_if_string(s: &(Any + Send)) {
    ///     if let Some(string) = s.downcast_ref::<String>() {
    ///         println!("It's a string({}): '{}'", string.len(), string);
    ///     } else {
    ///         println!("Not a string...");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     print_if_string(&0);
    ///     print_if_string(&"cookie monster".to_owned());
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        Any::downcast_ref::<T>(self)
    }

    /// Forwards to the method defined on the type `Any`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::Any;
    ///
    /// fn modify_if_u32(s: &mut (Any+ Send)) {
    ///     if let Some(num) = s.downcast_mut::<u32>() {
    ///         *num = 42;
    ///     }
    /// }
    ///
    /// fn main() {
    ///     let mut x = 10u32;
    ///     let mut s = "starlord".to_owned();
    ///
    ///     modify_if_u32(&mut x);
    ///     modify_if_u32(&mut s);
    ///
    ///     assert_eq!(x, 42);
    ///     assert_eq!(&s, "starlord");
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline]
    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
        Any::downcast_mut::<T>(self)
    }
}

///////////////////////////////////////////////////////////////////////////////
// TypeID and its methods
///////////////////////////////////////////////////////////////////////////////

/// A `TypeId` represents a globally unique identifier for a type.
///
/// Each `TypeId` is an opaque object which does not allow inspection of what's
/// inside but does allow basic operations such as cloning, comparison,
/// printing, and showing.
///
/// A `TypeId` is currently only available for types which ascribe to `'static`,
/// but this limitation may be removed in the future.
///
/// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth
/// noting that the hashes and ordering will vary between Rust releases. Beware
/// of relying on them outside of your code!
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct TypeId {
    t: u64,
}

impl TypeId {
    /// Returns the `TypeId` of the type this generic function has been
    /// instantiated with.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::any::{Any, TypeId};
    ///
    /// fn is_string<T: ?Sized + Any>(_s: &T) -> bool {
    ///     TypeId::of::<String>() == TypeId::of::<T>()
    /// }
    ///
    /// fn main() {
    ///     assert_eq!(is_string(&0), false);
    ///     assert_eq!(is_string(&"cookie monster".to_owned()), true);
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn of<T: ?Sized + 'static>() -> TypeId {
        TypeId {
            t: unsafe { intrinsics::type_id::<T>() },
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The `Ord` and `Eq` comparison traits

This module contains the definition of both `Ord` and `Eq` which define
the common interfaces for doing comparison. Both are language items
that the compiler uses to implement the comparison operators. Rust code
may implement `Ord` to overload the `<`, `<=`, `>`, and `>=` operators,
and `Eq` to overload the `==` and `!=` operators.

*/

/**
* Trait for values that can be compared for equality and inequality.
*
* This trait allows partial equality, where types can be unordered instead of strictly equal or
* unequal. For example, with the built-in floating-point types `a == b` and `a != b` will both
* evaluate to false if either `a` or `b` is NaN (cf. IEEE 754-2008 section 5.11).
*
* Eventually, this will be implemented by default for types that implement `TotalEq`.
*/
#[lang="eq"]
pub trait Eq {
    fn eq(&self, other: &Self) -> bool;
    fn ne(&self, other: &Self) -> bool;
}

/// Trait for equality comparisons where `a == b` and `a != b` are strict inverses.
pub trait TotalEq {
    fn equals(&self, other: &Self) -> bool;
}

macro_rules! totaleq_impl(
    ($t:ty) => {
        impl TotalEq for $t {
            #[inline(always)]
            fn equals(&self, other: &$t) -> bool { *self == *other }
        }
    }
)

totaleq_impl!(bool)

totaleq_impl!(u8)
totaleq_impl!(u16)
totaleq_impl!(u32)
totaleq_impl!(u64)

totaleq_impl!(i8)
totaleq_impl!(i16)
totaleq_impl!(i32)
totaleq_impl!(i64)

totaleq_impl!(int)
totaleq_impl!(uint)

#[deriving(Clone, Eq)]
pub enum Ordering { Less = -1, Equal = 0, Greater = 1 }

/// Trait for types that form a total order
pub trait TotalOrd: TotalEq {
    fn cmp(&self, other: &Self) -> Ordering;
}

impl TotalOrd for Ordering {
    #[inline(always)]
    fn cmp(&self, other: &Ordering) -> Ordering {
        (*self as int).cmp(&(*other as int))
    }
}

impl Ord for Ordering {
    #[inline(always)]
    fn lt(&self, other: &Ordering) -> bool { (*self as int) < (*other as int) }
    #[inline(always)]
    fn le(&self, other: &Ordering) -> bool { (*self as int) <= (*other as int) }
    #[inline(always)]
    fn gt(&self, other: &Ordering) -> bool { (*self as int) > (*other as int) }
    #[inline(always)]
    fn ge(&self, other: &Ordering) -> bool { (*self as int) >= (*other as int) }
}

macro_rules! totalord_impl(
    ($t:ty) => {
        impl TotalOrd for $t {
            #[inline(always)]
            fn cmp(&self, other: &$t) -> Ordering {
                if *self < *other { Less }
                else if *self > *other { Greater }
                else { Equal }
            }
        }
    }
)

totalord_impl!(u8)
totalord_impl!(u16)
totalord_impl!(u32)
totalord_impl!(u64)

totalord_impl!(i8)
totalord_impl!(i16)
totalord_impl!(i32)
totalord_impl!(i64)

totalord_impl!(int)
totalord_impl!(uint)

/**
* Trait for values that can be compared for a sort-order.
*
* Eventually this may be simplified to only require
* an `le` method, with the others generated from
* default implementations. However it should remain
* possible to implement the others separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="ord"]
pub trait Ord {
    fn lt(&self, other: &Self) -> bool;
    fn le(&self, other: &Self) -> bool;
    fn ge(&self, other: &Self) -> bool;
    fn gt(&self, other: &Self) -> bool;
}

#[inline(always)]
pub fn lt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).lt(v2)
}

#[inline(always)]
pub fn le<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).le(v2)
}

#[inline(always)]
pub fn eq<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).eq(v2)
}

#[inline(always)]
pub fn ne<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).ne(v2)
}

#[inline(always)]
pub fn ge<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).ge(v2)
}

#[inline(always)]
pub fn gt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).gt(v2)
}

/// The equivalence relation. Two values may be equivalent even if they are
/// of different types. The most common use case for this relation is
/// container types; e.g. it is often desirable to be able to use `&str`
/// values to look up entries in a container with `~str` keys.
pub trait Equiv<T> {
    fn equiv(&self, other: &T) -> bool;
}

#[inline(always)]
pub fn min<T:Ord>(v1: T, v2: T) -> T {
    if v1 < v2 { v1 } else { v2 }
}

#[inline(always)]
pub fn max<T:Ord>(v1: T, v2: T) -> T {
    if v1 > v2 { v1 } else { v2 }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_int_totalord() {
        assert_eq!(5.cmp(&10), Less);
        assert_eq!(10.cmp(&5), Greater);
        assert_eq!(5.cmp(&5), Equal);
        assert_eq!((-5).cmp(&12), Less);
        assert_eq!(12.cmp(-5), Greater);
    }

    #[test]
    fn test_int_totaleq() {
        assert!(5.equals(&5));
        assert!(!2.equals(&17));
    }

    #[test]
    fn test_ordering_order() {
        assert!(Less < Equal);
        assert_eq!(Greater.cmp(&Less), Greater);
    }
}
<commit_msg>libcore: combine cmp::Ordering instances in lexical order.<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The `Ord` and `Eq` comparison traits

This module contains the definition of both `Ord` and `Eq` which define
the common interfaces for doing comparison. Both are language items
that the compiler uses to implement the comparison operators. Rust code
may implement `Ord` to overload the `<`, `<=`, `>`, and `>=` operators,
and `Eq` to overload the `==` and `!=` operators.

*/

/**
* Trait for values that can be compared for equality and inequality.
*
* This trait allows partial equality, where types can be unordered instead of strictly equal or
* unequal. For example, with the built-in floating-point types `a == b` and `a != b` will both
* evaluate to false if either `a` or `b` is NaN (cf. IEEE 754-2008 section 5.11).
*
* Eventually, this will be implemented by default for types that implement `TotalEq`.
*/
#[lang="eq"]
pub trait Eq {
    fn eq(&self, other: &Self) -> bool;
    fn ne(&self, other: &Self) -> bool;
}

/// Trait for equality comparisons where `a == b` and `a != b` are strict inverses.
pub trait TotalEq {
    fn equals(&self, other: &Self) -> bool;
}

macro_rules! totaleq_impl(
    ($t:ty) => {
        impl TotalEq for $t {
            #[inline(always)]
            fn equals(&self, other: &$t) -> bool { *self == *other }
        }
    }
)

totaleq_impl!(bool)

totaleq_impl!(u8)
totaleq_impl!(u16)
totaleq_impl!(u32)
totaleq_impl!(u64)

totaleq_impl!(i8)
totaleq_impl!(i16)
totaleq_impl!(i32)
totaleq_impl!(i64)

totaleq_impl!(int)
totaleq_impl!(uint)

#[deriving(Clone, Eq)]
pub enum Ordering { Less = -1, Equal = 0, Greater = 1 }

/// Trait for types that form a total order
pub trait TotalOrd: TotalEq {
    fn cmp(&self, other: &Self) -> Ordering;
}

impl TotalOrd for Ordering {
    #[inline(always)]
    fn cmp(&self, other: &Ordering) -> Ordering {
        (*self as int).cmp(&(*other as int))
    }
}

impl Ord for Ordering {
    #[inline(always)]
    fn lt(&self, other: &Ordering) -> bool { (*self as int) < (*other as int) }
    #[inline(always)]
    fn le(&self, other: &Ordering) -> bool { (*self as int) <= (*other as int) }
    #[inline(always)]
    fn gt(&self, other: &Ordering) -> bool { (*self as int) > (*other as int) }
    #[inline(always)]
    fn ge(&self, other: &Ordering) -> bool { (*self as int) >= (*other as int) }
}

macro_rules! totalord_impl(
    ($t:ty) => {
        impl TotalOrd for $t {
            #[inline(always)]
            fn cmp(&self, other: &$t) -> Ordering {
                if *self < *other { Less }
                else if *self > *other { Greater }
                else { Equal }
            }
        }
    }
)

totalord_impl!(u8)
totalord_impl!(u16)
totalord_impl!(u32)
totalord_impl!(u64)

totalord_impl!(i8)
totalord_impl!(i16)
totalord_impl!(i32)
totalord_impl!(i64)

totalord_impl!(int)
totalord_impl!(uint)

/**
Return `o1` if it is not `Equal`, otherwise `o2`. Simulates the
lexical ordering on a type `(int, int)`.
*/
// used in deriving code in libsyntax
#[inline(always)]
pub fn lexical_ordering(o1: Ordering, o2: Ordering) -> Ordering {
    match o1 {
        Equal => o2,
        _ => o1
    }
}

/**
* Trait for values that can be compared for a sort-order.
*
* Eventually this may be simplified to only require
* an `le` method, with the others generated from
* default implementations. However it should remain
* possible to implement the others separately, for
* compatibility with floating-point NaN semantics
* (cf. IEEE 754-2008 section 5.11).
*/
#[lang="ord"]
pub trait Ord {
    fn lt(&self, other: &Self) -> bool;
    fn le(&self, other: &Self) -> bool;
    fn ge(&self, other: &Self) -> bool;
    fn gt(&self, other: &Self) -> bool;
}

#[inline(always)]
pub fn lt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).lt(v2)
}

#[inline(always)]
pub fn le<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).le(v2)
}

#[inline(always)]
pub fn eq<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).eq(v2)
}

#[inline(always)]
pub fn ne<T:Eq>(v1: &T, v2: &T) -> bool {
    (*v1).ne(v2)
}

#[inline(always)]
pub fn ge<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).ge(v2)
}

#[inline(always)]
pub fn gt<T:Ord>(v1: &T, v2: &T) -> bool {
    (*v1).gt(v2)
}

/// The equivalence relation. Two values may be equivalent even if they are
/// of different types. The most common use case for this relation is
/// container types; e.g. it is often desirable to be able to use `&str`
/// values to look up entries in a container with `~str` keys.
pub trait Equiv<T> {
    fn equiv(&self, other: &T) -> bool;
}

#[inline(always)]
pub fn min<T:Ord>(v1: T, v2: T) -> T {
    if v1 < v2 { v1 } else { v2 }
}

#[inline(always)]
pub fn max<T:Ord>(v1: T, v2: T) -> T {
    if v1 > v2 { v1 } else { v2 }
}

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

    #[test]
    fn test_int_totalord() {
        assert_eq!(5.cmp(&10), Less);
        assert_eq!(10.cmp(&5), Greater);
        assert_eq!(5.cmp(&5), Equal);
        assert_eq!((-5).cmp(&12), Less);
        assert_eq!(12.cmp(-5), Greater);
    }

    #[test]
    fn test_int_totaleq() {
        assert!(5.equals(&5));
        assert!(!2.equals(&17));
    }

    #[test]
    fn test_ordering_order() {
        assert!(Less < Equal);
        assert_eq!(Greater.cmp(&Less), Greater);
    }

    #[test]
    fn test_lexical_ordering() {
        fn t(o1: Ordering, o2: Ordering, e: Ordering) {
            assert_eq!(lexical_ordering(o1, o2), e);
        }
        for [Less, Equal, Greater].each |&o| {
            t(Less, o, Less);
            t(Equal, o, o);
            t(Greater, o, Greater);
         }
    }
}
<|endoftext|>


<commit_before>use hyper::Client;
use hyper::Result as HyperResult;
use hyper::client::Response as HyperResponse;
use hyper::status::StatusCode;
use hyper::header::Headers;

use types::Result;
use error::HesError;
use error::UserErrorKind;
use connection::ConnectionInfo;
use expected_version::ExpectedVersion;
use api::ESExpectedVersion;
use api::ESHardDelete;
use api::to_error::*;

pub struct Deleter<'a> {
    connection_info: &'a ConnectionInfo,
}

impl<'a> Deleter<'a> {
    pub fn new(connection_info: &'a ConnectionInfo) -> Deleter {
        Deleter { connection_info: connection_info }
    }

    pub fn delete(&self, stream_name: &str, expected_version: ExpectedVersion) -> Result<()> {
        self.do_delete(stream_name, expected_version, false)
    }

    pub fn hard_delete(&self, stream_name: &str, expected_version: ExpectedVersion) -> Result<()> {
        self.do_delete(stream_name, expected_version, true)
    }

    fn do_delete(&self,
                 stream_name: &str,
                 expected_version: ExpectedVersion,
                 is_hard: bool)
                 -> Result<()> {
        let http_client = Client::default();

        let result = http_client.delete(&self.url(stream_name))
            .headers(build_headers(expected_version, is_hard))
            .send();

        to_hes_result(result)
    }

    fn url(&self, stream_name: &str) -> String {
        format!("http://{}:{}/streams/{}",
                self.connection_info.host,
                self.connection_info.port,
                stream_name)
    }
}

fn build_headers(expected_version: ExpectedVersion, is_hard: bool) -> Headers {
    let mut headers = Headers::new();
    headers.set(ESExpectedVersion(expected_version.into()));
    headers.set(ESHardDelete(is_hard));

    headers
}

fn to_hes_result(result: HyperResult<HyperResponse>) -> Result<()> {
    match result {
        Ok(response) => {
            match response.status {
                StatusCode::NoContent => Ok(()),
                _ => stream_deleted_error(response)
                    .and_then(event_number_mismatch_error)
                    .map_err(|kind| HesError::UserError(kind))
                    .and_then(default_error)
            }
        },
        Err(err) => Err(HesError::UserError(UserErrorKind::Http(err)))
    }
}
<commit_msg>Use `impl From<hyper::error::Error> for HesError` to simplify  Appender and Deleter<commit_after>use hyper::Client;
use hyper::client::Response as HyperResponse;
use hyper::status::StatusCode;
use hyper::header::Headers;

use types::Result;
use error::HesError;
use connection::ConnectionInfo;
use expected_version::ExpectedVersion;
use api::ESExpectedVersion;
use api::ESHardDelete;
use api::to_error::*;

pub struct Deleter<'a> {
    connection_info: &'a ConnectionInfo,
}

impl<'a> Deleter<'a> {
    pub fn new(connection_info: &'a ConnectionInfo) -> Deleter {
        Deleter { connection_info: connection_info }
    }

    pub fn delete(&self, stream_name: &str, expected_version: ExpectedVersion) -> Result<()> {
        self.do_delete(stream_name, expected_version, false)
    }

    pub fn hard_delete(&self, stream_name: &str, expected_version: ExpectedVersion) -> Result<()> {
        self.do_delete(stream_name, expected_version, true)
    }

    fn do_delete(&self,
                 stream_name: &str,
                 expected_version: ExpectedVersion,
                 is_hard: bool)
                 -> Result<()> {
        let http_client = Client::default();

        let response = try!(http_client.delete(&self.url(stream_name))
            .headers(build_headers(expected_version, is_hard))
            .send());

        to_hes_result(response)
    }

    fn url(&self, stream_name: &str) -> String {
        format!("http://{}:{}/streams/{}",
                self.connection_info.host,
                self.connection_info.port,
                stream_name)
    }
}

fn build_headers(expected_version: ExpectedVersion, is_hard: bool) -> Headers {
    let mut headers = Headers::new();
    headers.set(ESExpectedVersion(expected_version.into()));
    headers.set(ESHardDelete(is_hard));

    headers
}

fn to_hes_result(response: HyperResponse) -> Result<()> {
    match response.status {
        StatusCode::NoContent => Ok(()),
        _ => stream_deleted_error(response)
            .and_then(event_number_mismatch_error)
            .map_err(|kind| HesError::UserError(kind))
            .and_then(default_error)
    }
}
<|endoftext|>