<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.

//! Semantic version parsing and comparison.
//!
//! Semantic versioning (see http://semver.org/) is a set of rules for
//! assigning version numbers intended to convey meaning about what has
//! changed, and how much. A version number has five parts:
//!
//!  * Major number, updated for incompatible API changes
//!  * Minor number, updated for backwards-compatible API additions
//!  * Patch number, updated for backwards-compatible bugfixes
//!  * Pre-release information (optional), preceded by a hyphen (`-`)
//!  * Build metadata (optional), preceded by a plus sign (`+`)
//!
//! The three mandatory components are required to be decimal numbers. The
//! pre-release information and build metadata are required to be a
//! period-separated list of identifiers containing only alphanumeric
//! characters and hyphens.
//!
//! An example version number with all five components is
//! `0.8.1-rc.3.0+20130922.linux`.

use std::char;
use std::cmp;
use std::option::{Option, Some, None};
use std::to_str::ToStr;

/// An identifier in the pre-release or build metadata. If the identifier can
/// be parsed as a decimal value, it will be represented with `Numeric`.
#[deriving(Clone, Eq)]
#[allow(missing_doc)]
pub enum Identifier {
    Numeric(uint),
    AlphaNumeric(~str)
}

impl cmp::Ord for Identifier {
    #[inline]
    fn lt(&self, other: &Identifier) -> bool {
        match (self, other) {
            (&Numeric(a), &Numeric(b)) => a < b,
            (&Numeric(_), _) => true,
            (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
            (&AlphaNumeric(_), _) => false
        }
    }
}

impl ToStr for Identifier {
    #[inline]
    fn to_str(&self) -> ~str {
        match self {
            &Numeric(n) => n.to_str(),
            &AlphaNumeric(ref s) => s.to_str()
        }
    }
}

/// Represents a version number conforming to the semantic versioning scheme.
#[deriving(Clone, Eq)]
pub struct Version {
    /// The major version, to be incremented on incompatible changes.
    priv major: uint,
    /// The minor version, to be incremented when functionality is added in a
    /// backwards-compatible manner.
    priv minor: uint,
    /// The patch version, to be incremented when backwards-compatible bug
    /// fixes are made.
    priv patch: uint,
    /// The pre-release version identifier, if one exists.
    priv pre: ~[Identifier],
    /// The build metadata, ignored when determining version precedence.
    priv build: ~[Identifier],
}

impl ToStr for Version {
    #[inline]
    fn to_str(&self) -> ~str {
        let s = format!("{}.{}.{}", self.major, self.minor, self.patch);
        let s = if self.pre.is_empty() {
            s
        } else {
            format!("{}-{}", s, self.pre.map(|i| i.to_str()).connect("."))
        };
        if self.build.is_empty() {
            s
        } else {
            format!("{}+{}", s, self.build.map(|i| i.to_str()).connect("."))
        }
    }
}

impl cmp::Ord for Version {
    #[inline]
    fn lt(&self, other: &Version) -> bool {

        self.major < other.major ||

            (self.major == other.major &&
             self.minor < other.minor) ||

            (self.major == other.major &&
             self.minor == other.minor &&
             self.patch < other.patch) ||

            (self.major == other.major &&
             self.minor == other.minor &&
             self.patch == other.patch &&
             // NB: semver spec says 0.0.0-pre < 0.0.0
             // but the version of ord defined for vec
             // says that [] < [pre], so we alter it
             // here.
             (match (self.pre.len(), other.pre.len()) {
                 (0, 0) => false,
                 (0, _) => false,
                 (_, 0) => true,
                 (_, _) => self.pre < other.pre
             }))
    }

    #[inline]
    fn le(&self, other: &Version) -> bool {
        ! (other < self)
    }
    #[inline]
    fn gt(&self, other: &Version) -> bool {
        other < self
    }
    #[inline]
    fn ge(&self, other: &Version) -> bool {
        ! (self < other)
    }
}

condition! {
    bad_parse: () -> ();
}

fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
                        -> (~str, Option<char>) {
    let mut buf = ~"";
    let mut ch = rdr.next();
    loop {
        match ch {
            None => break,
            Some(c) if !pred(c) => break,
            Some(c) => {
                buf.push_char(c);
                ch = rdr.next();
            }
        }
    }
    if buf.is_empty() {
        bad_parse::cond.raise(())
    }
    debug!("extracted nonempty prefix: {}", buf);
    (buf, ch)
}

fn take_num<T: Iterator<char>>(rdr: &mut T) -> (uint, Option<char>) {
    let (s, ch) = take_nonempty_prefix(rdr, char::is_digit);
    match from_str::<uint>(s) {
        None => { bad_parse::cond.raise(()); (0, ch) },
        Some(i) => (i, ch)
    }
}

fn take_ident<T: Iterator<char>>(rdr: &mut T) -> (Identifier, Option<char>) {
    let (s,ch) = take_nonempty_prefix(rdr, char::is_alphanumeric);
    if s.chars().all(char::is_digit) {
        match from_str::<uint>(s) {
            None => { bad_parse::cond.raise(()); (Numeric(0), ch) },
            Some(i) => (Numeric(i), ch)
        }
    } else {
        (AlphaNumeric(s), ch)
    }
}

fn expect(ch: Option<char>, c: char) {
    if ch != Some(c) {
        bad_parse::cond.raise(())
    }
}

fn parse_iter<T: Iterator<char>>(rdr: &mut T) -> Version {
    let (major, ch) = take_num(rdr);
    expect(ch, '.');
    let (minor, ch) = take_num(rdr);
    expect(ch, '.');
    let (patch, ch) = take_num(rdr);

    let mut pre = ~[];
    let mut build = ~[];

    let mut ch = ch;
    if ch == Some('-') {
        loop {
            let (id, c) = take_ident(rdr);
            pre.push(id);
            ch = c;
            if ch != Some('.') { break; }
        }
    }

    if ch == Some('+') {
        loop {
            let (id, c) = take_ident(rdr);
            build.push(id);
            ch = c;
            if ch != Some('.') { break; }
        }
    }

    Version {
        major: major,
        minor: minor,
        patch: patch,
        pre: pre,
        build: build,
    }
}

/// Parse a string into a semver object.
pub fn parse(s: &str) -> Option<Version> {
    if !s.is_ascii() {
        return None;
    }
    let s = s.trim();
    let mut bad = false;
    bad_parse::cond.trap(|_| { debug!("bad"); bad = true }).inside(|| {
        let v = parse_iter(&mut s.chars());
        if bad || v.to_str() != s.to_owned() {
            None
        } else {
            Some(v)
        }
    })
}

#[test]
fn test_parse() {
    assert_eq!(parse(""), None);
    assert_eq!(parse("  "), None);
    assert_eq!(parse("1"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1.2.3-"), None);
    assert_eq!(parse("a.b.c"), None);
    assert_eq!(parse("1.2.3 abc"), None);

    assert!(parse("1.2.3") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[],
    }));
    assert!(parse("  1.2.3  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[],
    }));
    assert!(parse("1.2.3-alpha1") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[]
    }));
    assert!(parse("  1.2.3-alpha1  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[]
    }));
    assert!(parse("1.2.3+build5") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("  1.2.3+build5  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("1.2.3-alpha1+build5") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("  1.2.3-alpha1+build5  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)],
        build: ~[AlphaNumeric(~"build5"),
                 Numeric(7),
                 AlphaNumeric(~"3aedf")]
    }));

}

#[test]
fn test_eq() {
    assert_eq!(parse("1.2.3"), parse("1.2.3"));
    assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1"));
    assert_eq!(parse("1.2.3+build.42"), parse("1.2.3+build.42"));
    assert_eq!(parse("1.2.3-alpha1+42"), parse("1.2.3-alpha1+42"));
}

#[test]
fn test_ne() {
    assert!(parse("0.0.0")       != parse("0.0.1"));
    assert!(parse("0.0.0")       != parse("0.1.0"));
    assert!(parse("0.0.0")       != parse("1.0.0"));
    assert!(parse("1.2.3-alpha") != parse("1.2.3-beta"));
    assert!(parse("1.2.3+23")    != parse("1.2.3+42"));
}

#[test]
fn test_lt() {
    assert!(parse("0.0.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.0.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.2.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha1") < parse("1.2.3"));
    assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2"));
    assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2")));
    assert!(!(parse("1.2.3+23")     < parse("1.2.3+42")));
}

#[test]
fn test_le() {
    assert!(parse("0.0.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.0.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3+23")     <= parse("1.2.3+42"));
}

#[test]
fn test_gt() {
    assert!(parse("1.2.3-alpha2") > parse("0.0.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.0.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.2.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1"));
    assert!(parse("1.2.3")        > parse("1.2.3-alpha2"));
    assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2")));
    assert!(!(parse("1.2.3+23")     > parse("1.2.3+42")));
}

#[test]
fn test_ge() {
    assert!(parse("1.2.3-alpha2") >= parse("0.0.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.0.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3+23")     >= parse("1.2.3+42"));
}

#[test]
fn test_spec_order() {

    let vs = ["1.0.0-alpha",
              "1.0.0-alpha.1",
              "1.0.0-alpha.beta",
              "1.0.0-beta",
              "1.0.0-beta.2",
              "1.0.0-beta.11",
              "1.0.0-rc.1",
              "1.0.0"];
    let mut i = 1;
    while i < vs.len() {
        let a = parse(vs[i-1]).unwrap();
        let b = parse(vs[i]).unwrap();
        assert!(a < b);
        i += 1;
    }
}
<commit_msg>auto merge of #11296 : brson/rust/anti-cond, r=alexcrichton<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.

//! Semantic version parsing and comparison.
//!
//! Semantic versioning (see http://semver.org/) is a set of rules for
//! assigning version numbers intended to convey meaning about what has
//! changed, and how much. A version number has five parts:
//!
//!  * Major number, updated for incompatible API changes
//!  * Minor number, updated for backwards-compatible API additions
//!  * Patch number, updated for backwards-compatible bugfixes
//!  * Pre-release information (optional), preceded by a hyphen (`-`)
//!  * Build metadata (optional), preceded by a plus sign (`+`)
//!
//! The three mandatory components are required to be decimal numbers. The
//! pre-release information and build metadata are required to be a
//! period-separated list of identifiers containing only alphanumeric
//! characters and hyphens.
//!
//! An example version number with all five components is
//! `0.8.1-rc.3.0+20130922.linux`.

use std::char;
use std::cmp;
use std::option::{Option, Some, None};
use std::to_str::ToStr;

/// An identifier in the pre-release or build metadata. If the identifier can
/// be parsed as a decimal value, it will be represented with `Numeric`.
#[deriving(Clone, Eq)]
#[allow(missing_doc)]
pub enum Identifier {
    Numeric(uint),
    AlphaNumeric(~str)
}

impl cmp::Ord for Identifier {
    #[inline]
    fn lt(&self, other: &Identifier) -> bool {
        match (self, other) {
            (&Numeric(a), &Numeric(b)) => a < b,
            (&Numeric(_), _) => true,
            (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b,
            (&AlphaNumeric(_), _) => false
        }
    }
}

impl ToStr for Identifier {
    #[inline]
    fn to_str(&self) -> ~str {
        match self {
            &Numeric(n) => n.to_str(),
            &AlphaNumeric(ref s) => s.to_str()
        }
    }
}

/// Represents a version number conforming to the semantic versioning scheme.
#[deriving(Clone, Eq)]
pub struct Version {
    /// The major version, to be incremented on incompatible changes.
    priv major: uint,
    /// The minor version, to be incremented when functionality is added in a
    /// backwards-compatible manner.
    priv minor: uint,
    /// The patch version, to be incremented when backwards-compatible bug
    /// fixes are made.
    priv patch: uint,
    /// The pre-release version identifier, if one exists.
    priv pre: ~[Identifier],
    /// The build metadata, ignored when determining version precedence.
    priv build: ~[Identifier],
}

impl ToStr for Version {
    #[inline]
    fn to_str(&self) -> ~str {
        let s = format!("{}.{}.{}", self.major, self.minor, self.patch);
        let s = if self.pre.is_empty() {
            s
        } else {
            format!("{}-{}", s, self.pre.map(|i| i.to_str()).connect("."))
        };
        if self.build.is_empty() {
            s
        } else {
            format!("{}+{}", s, self.build.map(|i| i.to_str()).connect("."))
        }
    }
}

impl cmp::Ord for Version {
    #[inline]
    fn lt(&self, other: &Version) -> bool {

        self.major < other.major ||

            (self.major == other.major &&
             self.minor < other.minor) ||

            (self.major == other.major &&
             self.minor == other.minor &&
             self.patch < other.patch) ||

            (self.major == other.major &&
             self.minor == other.minor &&
             self.patch == other.patch &&
             // NB: semver spec says 0.0.0-pre < 0.0.0
             // but the version of ord defined for vec
             // says that [] < [pre], so we alter it
             // here.
             (match (self.pre.len(), other.pre.len()) {
                 (0, 0) => false,
                 (0, _) => false,
                 (_, 0) => true,
                 (_, _) => self.pre < other.pre
             }))
    }

    #[inline]
    fn le(&self, other: &Version) -> bool {
        ! (other < self)
    }
    #[inline]
    fn gt(&self, other: &Version) -> bool {
        other < self
    }
    #[inline]
    fn ge(&self, other: &Version) -> bool {
        ! (self < other)
    }
}

fn take_nonempty_prefix<T:Iterator<char>>(rdr: &mut T, pred: |char| -> bool)
                        -> (~str, Option<char>) {
    let mut buf = ~"";
    let mut ch = rdr.next();
    loop {
        match ch {
            None => break,
            Some(c) if !pred(c) => break,
            Some(c) => {
                buf.push_char(c);
                ch = rdr.next();
            }
        }
    }
    debug!("extracted nonempty prefix: {}", buf);
    (buf, ch)
}

fn take_num<T: Iterator<char>>(rdr: &mut T) -> Option<(uint, Option<char>)> {
    let (s, ch) = take_nonempty_prefix(rdr, char::is_digit);
    match from_str::<uint>(s) {
        None => None,
        Some(i) => Some((i, ch))
    }
}

fn take_ident<T: Iterator<char>>(rdr: &mut T) -> Option<(Identifier, Option<char>)> {
    let (s,ch) = take_nonempty_prefix(rdr, char::is_alphanumeric);
    if s.chars().all(char::is_digit) {
        match from_str::<uint>(s) {
            None => None,
            Some(i) => Some((Numeric(i), ch))
        }
    } else {
        Some((AlphaNumeric(s), ch))
    }
}

fn expect(ch: Option<char>, c: char) -> Option<()> {
    if ch != Some(c) {
        None
    } else {
        Some(())
    }
}

fn parse_iter<T: Iterator<char>>(rdr: &mut T) -> Option<Version> {
    let maybe_vers = take_num(rdr).and_then(|(major, ch)| {
        expect(ch, '.').and_then(|_| Some(major))
    }).and_then(|major| {
        take_num(rdr).and_then(|(minor, ch)| {
            expect(ch, '.').and_then(|_| Some((major, minor)))
        })
    }).and_then(|(major, minor)| {
        take_num(rdr).and_then(|(patch, ch)| {
           Some((major, minor, patch, ch))
        })
    });

    let (major, minor, patch, ch) = match maybe_vers {
        Some((a, b, c, d)) => (a, b, c, d),
        None => return None
    };

    let mut pre = ~[];
    let mut build = ~[];

    let mut ch = ch;
    if ch == Some('-') {
        loop {
            let (id, c) = match take_ident(rdr) {
                Some((id, c)) => (id, c),
                None => return None
            };
            pre.push(id);
            ch = c;
            if ch != Some('.') { break; }
        }
    }

    if ch == Some('+') {
        loop {
            let (id, c) = match take_ident(rdr) {
                Some((id, c)) => (id, c),
                None => return None
            };
            build.push(id);
            ch = c;
            if ch != Some('.') { break; }
        }
    }

    Some(Version {
        major: major,
        minor: minor,
        patch: patch,
        pre: pre,
        build: build,
    })
}

/// Parse a string into a semver object.
pub fn parse(s: &str) -> Option<Version> {
    if !s.is_ascii() {
        return None;
    }
    let s = s.trim();
    let v = parse_iter(&mut s.chars());
    match v {
        Some(v) => {
            if v.to_str().equiv(&s) {
                Some(v)
            } else {
                None
            }
        }
        None => None
    }
}

#[test]
fn test_parse() {
    assert_eq!(parse(""), None);
    assert_eq!(parse("  "), None);
    assert_eq!(parse("1"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1"), None);
    assert_eq!(parse("1.2"), None);
    assert_eq!(parse("1.2.3-"), None);
    assert_eq!(parse("a.b.c"), None);
    assert_eq!(parse("1.2.3 abc"), None);

    assert!(parse("1.2.3") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[],
    }));
    assert!(parse("  1.2.3  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[],
    }));
    assert!(parse("1.2.3-alpha1") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[]
    }));
    assert!(parse("  1.2.3-alpha1  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[]
    }));
    assert!(parse("1.2.3+build5") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("  1.2.3+build5  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("1.2.3-alpha1+build5") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("  1.2.3-alpha1+build5  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[AlphaNumeric(~"alpha1")],
        build: ~[AlphaNumeric(~"build5")]
    }));
    assert!(parse("1.2.3-1.alpha1.9+build5.7.3aedf  ") == Some(Version {
        major: 1u,
        minor: 2u,
        patch: 3u,
        pre: ~[Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)],
        build: ~[AlphaNumeric(~"build5"),
                 Numeric(7),
                 AlphaNumeric(~"3aedf")]
    }));

}

#[test]
fn test_eq() {
    assert_eq!(parse("1.2.3"), parse("1.2.3"));
    assert_eq!(parse("1.2.3-alpha1"), parse("1.2.3-alpha1"));
    assert_eq!(parse("1.2.3+build.42"), parse("1.2.3+build.42"));
    assert_eq!(parse("1.2.3-alpha1+42"), parse("1.2.3-alpha1+42"));
}

#[test]
fn test_ne() {
    assert!(parse("0.0.0")       != parse("0.0.1"));
    assert!(parse("0.0.0")       != parse("0.1.0"));
    assert!(parse("0.0.0")       != parse("1.0.0"));
    assert!(parse("1.2.3-alpha") != parse("1.2.3-beta"));
    assert!(parse("1.2.3+23")    != parse("1.2.3+42"));
}

#[test]
fn test_lt() {
    assert!(parse("0.0.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.0.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.2.0")        < parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha1") < parse("1.2.3"));
    assert!(parse("1.2.3-alpha1") < parse("1.2.3-alpha2"));
    assert!(!(parse("1.2.3-alpha2") < parse("1.2.3-alpha2")));
    assert!(!(parse("1.2.3+23")     < parse("1.2.3+42")));
}

#[test]
fn test_le() {
    assert!(parse("0.0.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.0.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.0")        <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha1") <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3-alpha2") <= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3+23")     <= parse("1.2.3+42"));
}

#[test]
fn test_gt() {
    assert!(parse("1.2.3-alpha2") > parse("0.0.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.0.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.2.0"));
    assert!(parse("1.2.3-alpha2") > parse("1.2.3-alpha1"));
    assert!(parse("1.2.3")        > parse("1.2.3-alpha2"));
    assert!(!(parse("1.2.3-alpha2") > parse("1.2.3-alpha2")));
    assert!(!(parse("1.2.3+23")     > parse("1.2.3+42")));
}

#[test]
fn test_ge() {
    assert!(parse("1.2.3-alpha2") >= parse("0.0.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.0.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.0"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha1"));
    assert!(parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"));
    assert!(parse("1.2.3+23")     >= parse("1.2.3+42"));
}

#[test]
fn test_spec_order() {

    let vs = ["1.0.0-alpha",
              "1.0.0-alpha.1",
              "1.0.0-alpha.beta",
              "1.0.0-beta",
              "1.0.0-beta.2",
              "1.0.0-beta.11",
              "1.0.0-rc.1",
              "1.0.0"];
    let mut i = 1;
    while i < vs.len() {
        let a = parse(vs[i-1]).unwrap();
        let b = parse(vs[i]).unwrap();
        assert!(a < b);
        i += 1;
    }
}
<|endoftext|>


<commit_before>#[ macro_use ]
extern crate clap;

extern crate btrfs;
extern crate sha2;
extern crate termion;

#[ doc (hidden) ]
#[ macro_use ]
mod misc;

mod examine;
mod output;
mod scan;
mod types;

use std::path::PathBuf;
use std::process;

use types::*;
use output::Output;

fn main () {

	let arguments =
		parse_arguments ();

	let mut output =
		Output::new ();

	match main_real (
		& arguments,
		& mut output,
	) {

		Ok (_) => (),

		Err (error_message) => {

			output.clear_status ();

			output.message (
				& format! (
					"Error: {}",
					error_message));

			process::exit (1);

		},

	}

}

fn parse_arguments (
) -> Arguments {

	let argument_matches = (
		clap::App::new ("Btrfs Dedupe")

		.arg (
			clap::Arg::with_name ("match-filename")
				.long ("match-filename")
				.help ("Match filename as well as checksum")
		)

		.arg (
			clap::Arg::with_name ("root-path")
				.multiple (true)
				.value_name ("PATH")
				.help ("Root path to scan for files")
		)

	).get_matches ();

	let match_filename =
		argument_matches.is_present (
			"match-filename",
		);

	let root_paths = (
		argument_matches.values_of_os (
			"root-path",
		)
	).map (
		|os_values|

		os_values.map (
			|os_value|

			PathBuf::from (os_value)

		).collect ()

	).unwrap_or (
		Vec::new (),
	);

	Arguments {
		match_filename: match_filename,
		root_paths: root_paths,
	}

}

fn main_real (
	arguments: & Arguments,
	output: & mut Output,
) -> Result <(), String> {

	// create a list of all files with the same name and size

	let file_metadata_lists: FileMetadataLists = try! (
		scan::scan_directories (
			& arguments,
			output)
	);

	// discard any with only a single coincidence

	let file_metadata_lists: FileMetadataLists =
		file_metadata_lists.into_iter ().filter (
			|& (_, ref value)|

		value.len () > 1

	).collect ();

	output.message (
		& format! (
			"Found {} {} that coincide",
			file_metadata_lists.len (),
			if arguments.match_filename {
				"filenames and sizes"
			} else {
				"sizes"
			}));

	// perform a checksum on each one

	let (hash_lists, checksum_error_count) =
		examine::split_by_hash (
			output,
			file_metadata_lists);

	output.message (
		& format! (
			"Found {} {} that coincide",
			hash_lists.len (),
			if arguments.match_filename {
				"filenames and hashes"
			} else {
				"checksums"
			}));

	if checksum_error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors while calculating checksums",
				checksum_error_count));

	}

	if hash_lists.is_empty () {
		return Ok (());
	}

	// deduplicate the matches

	let mut progress: usize = 0;
	let target = hash_lists.len ();

	let mut error_count: u64 = 0;

	output.status (
		"Deduplication progress: 0%");

	for (_, paths)
	in hash_lists.into_iter () {

		let (first_path_slice, other_paths) =
			paths.split_at (1);

		let first_path =
			first_path_slice.into_iter ().next ().unwrap ();

		for paths_chunk in other_paths.chunks (512) {

			match btrfs::deduplicate_files_with_source (
				first_path,
				paths_chunk,
			) {

				Ok (_) => (),
				Err (_) => error_count += 1,

			}

		}

		progress += 1;

		if progress % 256 == 0 {

			output.status (
				& format! (
					"Deduplication progress: {}%",
					progress * 100 / target));

		}

	};

	output.clear_status ();

	if error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors during deduplication",
				error_count));

	} else {

		output.message (
			& format! (
				"Deduplicated all files successfully"));

	}

	// return

	Ok (())

}

// ex: noet ts=4 filetype=rust
<commit_msg>add extra information to --help<commit_after>#[ macro_use ]
extern crate clap;

extern crate btrfs;
extern crate sha2;
extern crate termion;

#[ doc (hidden) ]
#[ macro_use ]
mod misc;

mod examine;
mod output;
mod scan;
mod types;

use std::path::PathBuf;
use std::process;

use types::*;
use output::Output;

fn main () {

	let arguments =
		parse_arguments ();

	let mut output =
		Output::new ();

	match main_real (
		& arguments,
		& mut output,
	) {

		Ok (_) => (),

		Err (error_message) => {

			output.clear_status ();

			output.message (
				& format! (
					"Error: {}",
					error_message));

			process::exit (1);

		},

	}

}

fn parse_arguments (
) -> Arguments {

	let argument_matches = (
		clap::App::new ("Btrfs Dedupe")

		.about (
			"Deduplicates identical files on BTRFS file systems.\n\nPlease \
			visit https://gitlab.wellbehavedsoftware.com/well-behaved-software\
			/wbs-backup/tree/master/btrfs-dedupe for more information.")

		.author (
			"James Pharaoh <james@wellbehavedsoftware.com")

		.arg (
			clap::Arg::with_name ("match-filename")
				.long ("match-filename")
				.help ("Match filename as well as checksum")
		)

		.arg (
			clap::Arg::with_name ("root-path")
				.multiple (true)
				.value_name ("PATH")
				.help ("Root path to scan for files")
		)

	).get_matches ();

	let match_filename =
		argument_matches.is_present (
			"match-filename",
		);

	let root_paths = (
		argument_matches.values_of_os (
			"root-path",
		)
	).map (
		|os_values|

		os_values.map (
			|os_value|

			PathBuf::from (os_value)

		).collect ()

	).unwrap_or (
		Vec::new (),
	);

	Arguments {
		match_filename: match_filename,
		root_paths: root_paths,
	}

}

fn main_real (
	arguments: & Arguments,
	output: & mut Output,
) -> Result <(), String> {

	// create a list of all files with the same name and size

	let file_metadata_lists: FileMetadataLists = try! (
		scan::scan_directories (
			& arguments,
			output)
	);

	// discard any with only a single coincidence

	let file_metadata_lists: FileMetadataLists =
		file_metadata_lists.into_iter ().filter (
			|& (_, ref value)|

		value.len () > 1

	).collect ();

	output.message (
		& format! (
			"Found {} {} that coincide",
			file_metadata_lists.len (),
			if arguments.match_filename {
				"filenames and sizes"
			} else {
				"sizes"
			}));

	// perform a checksum on each one

	let (hash_lists, checksum_error_count) =
		examine::split_by_hash (
			output,
			file_metadata_lists);

	output.message (
		& format! (
			"Found {} {} that coincide",
			hash_lists.len (),
			if arguments.match_filename {
				"filenames and hashes"
			} else {
				"checksums"
			}));

	if checksum_error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors while calculating checksums",
				checksum_error_count));

	}

	if hash_lists.is_empty () {
		return Ok (());
	}

	// deduplicate the matches

	let mut progress: usize = 0;
	let target = hash_lists.len ();

	let mut error_count: u64 = 0;

	output.status (
		"Deduplication progress: 0%");

	for (_, paths)
	in hash_lists.into_iter () {

		let (first_path_slice, other_paths) =
			paths.split_at (1);

		let first_path =
			first_path_slice.into_iter ().next ().unwrap ();

		for paths_chunk in other_paths.chunks (512) {

			match btrfs::deduplicate_files_with_source (
				first_path,
				paths_chunk,
			) {

				Ok (_) => (),
				Err (_) => error_count += 1,

			}

		}

		progress += 1;

		if progress % 256 == 0 {

			output.status (
				& format! (
					"Deduplication progress: {}%",
					progress * 100 / target));

		}

	};

	output.clear_status ();

	if error_count > 0 {

		output.message (
			& format! (
				"Encountered {} errors during deduplication",
				error_count));

	} else {

		output.message (
			& format! (
				"Deduplicated all files successfully"));

	}

	// return

	Ok (())

}

// ex: noet ts=4 filetype=rust
<|endoftext|>


<commit_before>//! Some iterator that produces tuples

use std::iter::Fuse;
use std::iter::Take;
use std::iter::Cycle;
use std::marker::PhantomData;

// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
// tuple-related methods to be used by clients in generic contexts, while
// hiding the implementation details of `TupleCollect`.
// See https://github.com/rust-itertools/itertools/issues/387

/// Implemented for homogeneous tuples of size up to 4.
pub trait HomogeneousTuple
    : TupleCollect
{}

impl<T: TupleCollect> HomogeneousTuple for T {}

/// An iterator over a incomplete tuple.
///
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) and
/// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer).
#[derive(Clone, Debug)]
pub struct TupleBuffer<T>
    where T: HomogeneousTuple
{
    cur: usize,
    buf: T::Buffer,
}

impl<T> TupleBuffer<T>
    where T: HomogeneousTuple
{
    fn new(buf: T::Buffer) -> Self {
        TupleBuffer {
            cur: 0,
            buf,
        }
    }
}

impl<T> Iterator for TupleBuffer<T>
    where T: HomogeneousTuple
{
    type Item = T::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let s = self.buf.as_mut();
        if let Some(ref mut item) = s.get_mut(self.cur) {
            self.cur += 1;
            item.take()
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let buffer = &self.buf.as_ref()[self.cur..];
        let len = if buffer.is_empty() {
            0
        } else {
            buffer.iter()
                  .position(|x| x.is_none())
                  .unwrap_or_else(|| buffer.len())
        };
        (len, Some(len))
    }
}

impl<T> ExactSizeIterator for TupleBuffer<T>
    where T: HomogeneousTuple
{
}

/// An iterator that groups the items in tuples of a specific size.
///
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    iter: Fuse<I>,
    buf: T::Buffer,
}

/// Create a new tuples iterator.
pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    Tuples {
        iter: iter.fuse(),
        buf: Default::default(),
    }
}

impl<I, T> Iterator for Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        T::collect_from_iter(&mut self.iter, &mut self.buf)
    }
}

impl<I, T> Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    /// Return a buffer with the produced items that was not enough to be grouped in a tuple.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let mut iter = (0..5).tuples();
    /// assert_eq!(Some((0, 1, 2)), iter.next());
    /// assert_eq!(None, iter.next());
    /// itertools::assert_equal(vec![3, 4], iter.into_buffer());
    /// ```
    pub fn into_buffer(self) -> TupleBuffer<T> {
        TupleBuffer::new(self.buf)
    }
}

/// An iterator over all contiguous windows that produces tuples of a specific size.
///
/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    iter: I,
    last: Option<T>,
}

/// Create a new tuple windows iterator.
pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple,
          T::Item: Clone
{
    use std::iter::once;

    let mut last = None;
    if T::num_items() != 1 {
        // put in a duplicate item in front of the tuple; this simplifies
        // .next() function.
        if let Some(item) = iter.next() {
            let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
            last = T::collect_from_iter_no_buf(iter);
        }
    }

    TupleWindows {
        last,
        iter,
    }
}

impl<I, T> Iterator for TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple + Clone,
          T::Item: Clone
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if T::num_items() == 1 {
            return T::collect_from_iter_no_buf(&mut self.iter)
        }
        if let Some(ref mut last) = self.last {
            if let Some(new) = self.iter.next() {
                last.left_shift_push(new);
                return Some(last.clone());
            }
        }
        None
    }
}

/// An iterator over all windows,wrapping back to the first elements when the
/// window would otherwise exceed the length of the iterator, producing tuples
/// of a specific size.
///
/// See [`.circular_tuple_windows()`](../trait.Itertools.html#method.circular_tuple_windows) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct CircularTupleWindows<I, T: Clone>
    where I: Iterator<Item = T::Item> + Clone,
          T: TupleCollect + Clone
{
    iter: Take<TupleWindows<Cycle<I>, T>>,
    phantom_data: PhantomData<T>
}

pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
    where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
          T: TupleCollect + Clone,
          T::Item: Clone
{
    let len = iter.len();
    let iter = tuple_windows(iter.cycle()).take(len);

    CircularTupleWindows {
        iter,
        phantom_data: PhantomData{}
    }
}

impl<I, T> Iterator for CircularTupleWindows<I, T>
    where I: Iterator<Item = T::Item> + Clone,
          T: TupleCollect + Clone,
          T::Item: Clone
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }
}

pub trait TupleCollect: Sized {
    type Item;
    type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;

    fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
        where I: IntoIterator<Item = Self::Item>;

    fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
        where I: IntoIterator<Item = Self::Item>;

    fn num_items() -> usize;

    fn left_shift_push(&mut self, item: Self::Item);
}

macro_rules! count_ident{
    () => {0};
    ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
}
macro_rules! ignore_ident{
    ($id:ident, $($t:tt)*) => {$($t)*};
}

macro_rules! impl_tuple_collect {
    ($($Y:ident),* ; $($Y_rev:ident),*) => (
        impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
            type Item = A;
            type Buffer = [Option<A>; count_ident!($($Y,)*) - 1];

            #[allow(unused_assignments, unused_mut)]
            fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
                where I: IntoIterator<Item = A>
            {
                let mut iter = iter.into_iter();
                $(
                    let mut $Y = None;
                )*

                loop {
                    $(
                        $Y = iter.next();
                        if $Y.is_none() {
                            break
                        }
                    )*
                    return Some(($($Y.unwrap()),*,))
                }

                let mut i = 0;
                let mut s = buf.as_mut();
                $(
                    if i < s.len() {
                        s[i] = $Y;
                        i += 1;
                    }
                )*
                return None;
            }

            fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
                where I: IntoIterator<Item = A>
            {
                let mut iter = iter.into_iter();

                Some(($(
                    { let $Y = iter.next()?; $Y },
                )*))
            }

            fn num_items() -> usize {
                count_ident!($($Y,)*)
            }

            fn left_shift_push(&mut self, item: A) {
                use std::mem::replace;

                let &mut ($(ref mut $Y),*,) = self;
                $(
                    let item = replace($Y_rev, item);
                )*
                drop(item);
            }
        }
    )
}

// This snippet generates the twelve `impl_tuple_collect!` invocations:
//    use core::iter;
//    use itertools::Itertools;
//
//    for i in 1..=12 {
//        println!("impl_tuple_collect!({idents}; {rev_idents});",
//            idents=('a'..='z').take(i).join(", "),
//            rev_idents=('a'..='z').take(i).collect_vec().into_iter().rev().join(", ")
//        );
//    }
// It could probably be replaced by a bit more macro cleverness.
impl_tuple_collect!(a; a);
impl_tuple_collect!(a, b; b, a);
impl_tuple_collect!(a, b, c; c, b, a);
impl_tuple_collect!(a, b, c, d; d, c, b, a);
impl_tuple_collect!(a, b, c, d, e; e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f; f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g; g, f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g, h; h, g, f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i; i, h, g, f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j; j, i, h, g, f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j, k; k, j, i, h, g, f, e, d, c, b, a);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j, k, l; l, k, j, i, h, g, f, e, d, c, b, a);
<commit_msg>More macro cleverness (4) avoid Y_rev<commit_after>//! Some iterator that produces tuples

use std::iter::Fuse;
use std::iter::Take;
use std::iter::Cycle;
use std::marker::PhantomData;

// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
// tuple-related methods to be used by clients in generic contexts, while
// hiding the implementation details of `TupleCollect`.
// See https://github.com/rust-itertools/itertools/issues/387

/// Implemented for homogeneous tuples of size up to 4.
pub trait HomogeneousTuple
    : TupleCollect
{}

impl<T: TupleCollect> HomogeneousTuple for T {}

/// An iterator over a incomplete tuple.
///
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) and
/// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer).
#[derive(Clone, Debug)]
pub struct TupleBuffer<T>
    where T: HomogeneousTuple
{
    cur: usize,
    buf: T::Buffer,
}

impl<T> TupleBuffer<T>
    where T: HomogeneousTuple
{
    fn new(buf: T::Buffer) -> Self {
        TupleBuffer {
            cur: 0,
            buf,
        }
    }
}

impl<T> Iterator for TupleBuffer<T>
    where T: HomogeneousTuple
{
    type Item = T::Item;

    fn next(&mut self) -> Option<Self::Item> {
        let s = self.buf.as_mut();
        if let Some(ref mut item) = s.get_mut(self.cur) {
            self.cur += 1;
            item.take()
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let buffer = &self.buf.as_ref()[self.cur..];
        let len = if buffer.is_empty() {
            0
        } else {
            buffer.iter()
                  .position(|x| x.is_none())
                  .unwrap_or_else(|| buffer.len())
        };
        (len, Some(len))
    }
}

impl<T> ExactSizeIterator for TupleBuffer<T>
    where T: HomogeneousTuple
{
}

/// An iterator that groups the items in tuples of a specific size.
///
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    iter: Fuse<I>,
    buf: T::Buffer,
}

/// Create a new tuples iterator.
pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    Tuples {
        iter: iter.fuse(),
        buf: Default::default(),
    }
}

impl<I, T> Iterator for Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        T::collect_from_iter(&mut self.iter, &mut self.buf)
    }
}

impl<I, T> Tuples<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    /// Return a buffer with the produced items that was not enough to be grouped in a tuple.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let mut iter = (0..5).tuples();
    /// assert_eq!(Some((0, 1, 2)), iter.next());
    /// assert_eq!(None, iter.next());
    /// itertools::assert_equal(vec![3, 4], iter.into_buffer());
    /// ```
    pub fn into_buffer(self) -> TupleBuffer<T> {
        TupleBuffer::new(self.buf)
    }
}

/// An iterator over all contiguous windows that produces tuples of a specific size.
///
/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple
{
    iter: I,
    last: Option<T>,
}

/// Create a new tuple windows iterator.
pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple,
          T::Item: Clone
{
    use std::iter::once;

    let mut last = None;
    if T::num_items() != 1 {
        // put in a duplicate item in front of the tuple; this simplifies
        // .next() function.
        if let Some(item) = iter.next() {
            let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
            last = T::collect_from_iter_no_buf(iter);
        }
    }

    TupleWindows {
        last,
        iter,
    }
}

impl<I, T> Iterator for TupleWindows<I, T>
    where I: Iterator<Item = T::Item>,
          T: HomogeneousTuple + Clone,
          T::Item: Clone
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        if T::num_items() == 1 {
            return T::collect_from_iter_no_buf(&mut self.iter)
        }
        if let Some(ref mut last) = self.last {
            if let Some(new) = self.iter.next() {
                last.left_shift_push(new);
                return Some(last.clone());
            }
        }
        None
    }
}

/// An iterator over all windows,wrapping back to the first elements when the
/// window would otherwise exceed the length of the iterator, producing tuples
/// of a specific size.
///
/// See [`.circular_tuple_windows()`](../trait.Itertools.html#method.circular_tuple_windows) for more
/// information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct CircularTupleWindows<I, T: Clone>
    where I: Iterator<Item = T::Item> + Clone,
          T: TupleCollect + Clone
{
    iter: Take<TupleWindows<Cycle<I>, T>>,
    phantom_data: PhantomData<T>
}

pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
    where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
          T: TupleCollect + Clone,
          T::Item: Clone
{
    let len = iter.len();
    let iter = tuple_windows(iter.cycle()).take(len);

    CircularTupleWindows {
        iter,
        phantom_data: PhantomData{}
    }
}

impl<I, T> Iterator for CircularTupleWindows<I, T>
    where I: Iterator<Item = T::Item> + Clone,
          T: TupleCollect + Clone,
          T::Item: Clone
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }
}

pub trait TupleCollect: Sized {
    type Item;
    type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;

    fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
        where I: IntoIterator<Item = Self::Item>;

    fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
        where I: IntoIterator<Item = Self::Item>;

    fn num_items() -> usize;

    fn left_shift_push(&mut self, item: Self::Item);
}

macro_rules! count_ident{
    () => {0};
    ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
}
macro_rules! ignore_ident{
    ($id:ident, $($t:tt)*) => {$($t)*};
}
macro_rules! rev_for_each_ident{
    ($m:ident, ) => {};
    ($m:ident, $i0:ident, $($i:ident,)*) => {
        rev_for_each_ident!($m, $($i,)*);
        $m!($i0);
    };
}

macro_rules! impl_tuple_collect {
    ($($Y:ident),*) => (
        impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
            type Item = A;
            type Buffer = [Option<A>; count_ident!($($Y,)*) - 1];

            #[allow(unused_assignments, unused_mut)]
            fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
                where I: IntoIterator<Item = A>
            {
                let mut iter = iter.into_iter();
                $(
                    let mut $Y = None;
                )*

                loop {
                    $(
                        $Y = iter.next();
                        if $Y.is_none() {
                            break
                        }
                    )*
                    return Some(($($Y.unwrap()),*,))
                }

                let mut i = 0;
                let mut s = buf.as_mut();
                $(
                    if i < s.len() {
                        s[i] = $Y;
                        i += 1;
                    }
                )*
                return None;
            }

            fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
                where I: IntoIterator<Item = A>
            {
                let mut iter = iter.into_iter();

                Some(($(
                    { let $Y = iter.next()?; $Y },
                )*))
            }

            fn num_items() -> usize {
                count_ident!($($Y,)*)
            }

            fn left_shift_push(&mut self, mut item: A) {
                use std::mem::replace;

                let &mut ($(ref mut $Y),*,) = self;
                macro_rules! replace_item{($i:ident) => {
                    item = replace($i, item);
                }};
                rev_for_each_ident!(replace_item, $($Y,)*);
                drop(item);
            }
        }
    )
}

impl_tuple_collect!(a);
impl_tuple_collect!(a, b);
impl_tuple_collect!(a, b, c);
impl_tuple_collect!(a, b, c, d);
impl_tuple_collect!(a, b, c, d, e);
impl_tuple_collect!(a, b, c, d, e, f);
impl_tuple_collect!(a, b, c, d, e, f, g);
impl_tuple_collect!(a, b, c, d, e, f, g, h);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j, k);
impl_tuple_collect!(a, b, c, d, e, f, g, h, i, j, k, l);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec2, ImVec4};

use model::{FieldCoord, HexCoord, Model};

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

pub fn board(model: &mut Model, size: &ImVec2) {
    let mut cursor_pos = ImVec2::default();
    unsafe {
        imgui_sys::igGetCursorScreenPos(&mut cursor_pos);
    }

    // We want to fit the board into the size vector given us. Since the board is slightly taller
    // than it is wide, we take the height as our constraining dimension, and calculate the side
    // length of a triangle on the board from it.
    let side_len = size.y / (5.0 * SQRT_3);
    let origin = ImVec2::new(cursor_pos.x + size.x / 2.0, cursor_pos.y + size.y / 2.0);
    for hex in model.board.extant_hexes() {
        draw_hex(&hex, &origin, side_len);
    }

    unsafe {
        imgui_sys::igDummy(size);
    }
}

fn draw_hex(coord: &HexCoord, origin: &ImVec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let white = imgui_sys::igColorConvertFloat4ToU32(ImVec4::new(1.0, 1.0, 1.0, 1.0));
        let black = imgui_sys::igColorConvertFloat4ToU32(ImVec4::new(0.0, 0.0, 0.0, 1.0));

        let color = if coord.f() % 2 == 0 { white } else { black };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
    }
}

fn field_vertexes(coord: &FieldCoord, origin: &ImVec2, size: f32) -> (ImVec2, ImVec2, ImVec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size / 2.0, center.y - height);
        }
        1 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size, center.y);
        }
        2 => {
            v1 = ImVec2::new(center.x + size, center.y);
            v2 = ImVec2::new(center.x + size / 2.0, center.y + height);
        }
        3 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size / 2.0, center.y + height);
        }
        4 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size, center.y);
        }
        5 => {
            v1 = ImVec2::new(center.x - size, center.y);
            v2 = ImVec2::new(center.x - size / 2.0, center.y - height);
        }
        _ => panic!("You made the impossible possible."),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let x = coord.x() as f32;
    let y = coord.y() as f32;

    let px = size * (3.0 / 2.0) * x;
    let py = size * -SQRT_3 * (x / 2.0 + y);

    ImVec2::new(px + origin.x, py + origin.y)
}
<commit_msg>Add field hover highlight test<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec2, ImVec4};

use model::{FieldCoord, HexCoord, Model};

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

pub fn board(model: &mut Model, size: &ImVec2) {
    let mut mouse_pos = ImVec2::default();
    let mut cursor_pos = ImVec2::default();
    unsafe {
        imgui_sys::igGetMousePos(&mut mouse_pos);
        imgui_sys::igGetCursorScreenPos(&mut cursor_pos);
    }

    // We want to fit the board into the size vector given us. Since the board is slightly taller
    // than it is wide, we take the height as our constraining dimension, and calculate the side
    // length of a triangle on the board from it.
    let side_len = size.y / (5.0 * SQRT_3);
    let origin = ImVec2::new(cursor_pos.x + size.x / 2.0, cursor_pos.y + size.y / 2.0);
    for hex in model.board.extant_hexes() {
        draw_hex(&hex, &origin, side_len);
    }

    if let Some(coord) = pixel_to_field(&mouse_pos, &origin, side_len) {
        highlight_field(&coord, &origin, side_len);
    }

    unsafe {
        imgui_sys::igDummy(size);
    }
}

fn draw_hex(coord: &HexCoord, origin: &ImVec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let white = imgui_sys::igColorConvertFloat4ToU32(ImVec4::new(1.0, 1.0, 1.0, 1.0));
        let black = imgui_sys::igColorConvertFloat4ToU32(ImVec4::new(0.0, 0.0, 0.0, 1.0));

        let color = if coord.f() % 2 == 0 { white } else { black };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
    }
}

fn highlight_field(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let highlight = imgui_sys::igColorConvertFloat4ToU32(ImVec4::new(1.0, 1.0, 0.0, 0.7));

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, highlight);
    }
}

fn field_vertexes(coord: &FieldCoord, origin: &ImVec2, size: f32) -> (ImVec2, ImVec2, ImVec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size / 2.0, center.y - height);
        }
        1 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size, center.y);
        }
        2 => {
            v1 = ImVec2::new(center.x + size, center.y);
            v2 = ImVec2::new(center.x + size / 2.0, center.y + height);
        }
        3 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size / 2.0, center.y + height);
        }
        4 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size, center.y);
        }
        5 => {
            v1 = ImVec2::new(center.x - size, center.y);
            v2 = ImVec2::new(center.x - size / 2.0, center.y - height);
        }
        _ => panic!("You made the impossible possible."),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let x = coord.x() as f32;
    let y = coord.y() as f32;

    let px = size * (3.0 / 2.0) * x;
    let py = size * -SQRT_3 * (x / 2.0 + y);

    ImVec2::new(px + origin.x, py + origin.y)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
fn pixel_to_field(p: &ImVec2, origin: &ImVec2, size: f32) -> Option<FieldCoord> {
    let x = p.x - origin.x;
    let y = p.y - origin.y;

    let q = x * (2.0 / 3.0) / size;
    let r = (-x - SQRT_3 * y) / (size * 3.0);

    if let Some(hex) = round_hex_coord(q, r) {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - hex.x() as f32;
        let y_diff = r - hex.y() as f32;
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        // Using a lookup table because nested ifs are too confusing
        const INVALID: u32 = 6;
        let field_lookup = [3, 4, 2, INVALID, INVALID, 5, 1, 0];

        Some(hex.to_field(field_lookup[i]))
    } else {
        None
    }
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    let rx = rx as i32;
    let ry = ry as i32;

    if HexCoord::is_valid_coord(rx, ry) {
        Some(HexCoord::new(rx, ry))
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use cookie_factory::GenError;

use crate::{
    flags::AMQPFlags,
    types::{ByteArray, DecimalValue, FieldArray, FieldTable, LongString, LongStringRef, ShortString, ShortStringRef},
    value::AMQPValue,
};

/// Trait for computing the size required for serialization
pub trait GenSize {
    /// Compute the size required for serialization
    fn get_gen_size(&self) -> usize;
    /// Check that the buffer has enough room for serialization
    fn check_gen_size(&self, buffer: &[u8]) -> Result<(), GenError> {
        let size = self.get_gen_size();
        if buffer.len() < size {
            Err(GenError::BufferTooSmall(size))
        } else {
            Ok(())
        }
    }
}

/// Struct to compute the serialization size of length-prefixed values
pub struct Length;
struct ShortLength;

impl GenSize for Length {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for ShortLength {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for bool {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for u8 {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for u16 {
    fn get_gen_size(&self) -> usize {
        2
    }
}

impl GenSize for u32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for u64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for i8 {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for i16 {
    fn get_gen_size(&self) -> usize {
        2
    }
}

impl GenSize for i32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for i64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for f32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for f64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for str {
    fn get_gen_size(&self) -> usize {
        self.len()
    }
}

impl GenSize for ShortString {
    fn get_gen_size(&self) -> usize {
        self.as_ref().get_gen_size()
    }
}

impl<'a> GenSize for ShortStringRef<'a> {
    fn get_gen_size(&self) -> usize {
        ShortLength.get_gen_size() + self.0.get_gen_size()
    }
}

impl GenSize for LongString {
    fn get_gen_size(&self) -> usize {
        self.as_ref().get_gen_size()
    }
}

impl<'a> GenSize for LongStringRef<'a> {
    fn get_gen_size(&self) -> usize {
        ShortLength.get_gen_size() + self.0.get_gen_size()
    }
}

impl GenSize for DecimalValue {
    fn get_gen_size(&self) -> usize {
        self.scale.get_gen_size() + self.value.get_gen_size()
    }
}

impl GenSize for FieldArray {
    fn get_gen_size(&self) -> usize {
        Length.get_gen_size() + self.as_slice().get_gen_size()
    }
}

impl GenSize for [AMQPValue] {
    fn get_gen_size(&self) -> usize {
        self.iter().fold(0, |acc, val| acc + val.get_gen_size())
    }
}

impl GenSize for FieldTable {
    fn get_gen_size(&self) -> usize {
        self.iter().fold(Length.get_gen_size(), |acc, (k, v)| acc + k.get_gen_size() + v.get_gen_size())
    }
}

impl GenSize for ByteArray {
    fn get_gen_size(&self) -> usize {
        Length.get_gen_size() + self.as_slice().get_gen_size()
    }
}

impl GenSize for [u8] {
    fn get_gen_size(&self) -> usize {
        self.len()
    }
}

impl GenSize for AMQPFlags {
    fn get_gen_size(&self) -> usize {
        self.get_bytes().len()
    }
}

impl GenSize for AMQPValue {
    fn get_gen_size(&self) -> usize {
        ShortLength.get_gen_size() + match *self {
            AMQPValue::Boolean(b)         => b.get_gen_size(),
            AMQPValue::ShortShortInt(i)   => i.get_gen_size(),
            AMQPValue::ShortShortUInt(u)  => u.get_gen_size(),
            AMQPValue::ShortInt(i)        => i.get_gen_size(),
            AMQPValue::ShortUInt(u)       => u.get_gen_size(),
            AMQPValue::LongInt(i)         => i.get_gen_size(),
            AMQPValue::LongUInt(u)        => u.get_gen_size(),
            AMQPValue::LongLongInt(i)     => i.get_gen_size(),
            AMQPValue::Float(f)           => f.get_gen_size(),
            AMQPValue::Double(d)          => d.get_gen_size(),
            AMQPValue::DecimalValue(d)    => d.get_gen_size(),
            AMQPValue::ShortString(ref s) => s.get_gen_size(),
            AMQPValue::LongString(ref s)  => s.get_gen_size(),
            AMQPValue::FieldArray(ref a)  => a.get_gen_size(),
            AMQPValue::Timestamp(t)       => t.get_gen_size(),
            AMQPValue::FieldTable(ref t)  => t.get_gen_size(),
            AMQPValue::ByteArray(ref a)   => a.get_gen_size(),
            AMQPValue::Void               => 0,
        }
    }
}
<commit_msg>types: LongString GenSize fix<commit_after>use cookie_factory::GenError;

use crate::{
    flags::AMQPFlags,
    types::{ByteArray, DecimalValue, FieldArray, FieldTable, LongString, LongStringRef, ShortString, ShortStringRef},
    value::AMQPValue,
};

/// Trait for computing the size required for serialization
pub trait GenSize {
    /// Compute the size required for serialization
    fn get_gen_size(&self) -> usize;
    /// Check that the buffer has enough room for serialization
    fn check_gen_size(&self, buffer: &[u8]) -> Result<(), GenError> {
        let size = self.get_gen_size();
        if buffer.len() < size {
            Err(GenError::BufferTooSmall(size))
        } else {
            Ok(())
        }
    }
}

/// Struct to compute the serialization size of length-prefixed values
pub struct Length;
struct ShortLength;

impl GenSize for Length {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for ShortLength {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for bool {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for u8 {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for u16 {
    fn get_gen_size(&self) -> usize {
        2
    }
}

impl GenSize for u32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for u64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for i8 {
    fn get_gen_size(&self) -> usize {
        1
    }
}

impl GenSize for i16 {
    fn get_gen_size(&self) -> usize {
        2
    }
}

impl GenSize for i32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for i64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for f32 {
    fn get_gen_size(&self) -> usize {
        4
    }
}

impl GenSize for f64 {
    fn get_gen_size(&self) -> usize {
        8
    }
}

impl GenSize for str {
    fn get_gen_size(&self) -> usize {
        self.len()
    }
}

impl GenSize for ShortString {
    fn get_gen_size(&self) -> usize {
        self.as_ref().get_gen_size()
    }
}

impl<'a> GenSize for ShortStringRef<'a> {
    fn get_gen_size(&self) -> usize {
        ShortLength.get_gen_size() + self.0.get_gen_size()
    }
}

impl GenSize for LongString {
    fn get_gen_size(&self) -> usize {
        self.as_ref().get_gen_size()
    }
}

impl<'a> GenSize for LongStringRef<'a> {
    fn get_gen_size(&self) -> usize {
        Length.get_gen_size() + self.0.get_gen_size()
    }
}

impl GenSize for DecimalValue {
    fn get_gen_size(&self) -> usize {
        self.scale.get_gen_size() + self.value.get_gen_size()
    }
}

impl GenSize for FieldArray {
    fn get_gen_size(&self) -> usize {
        Length.get_gen_size() + self.as_slice().get_gen_size()
    }
}

impl GenSize for [AMQPValue] {
    fn get_gen_size(&self) -> usize {
        self.iter().fold(0, |acc, val| acc + val.get_gen_size())
    }
}

impl GenSize for FieldTable {
    fn get_gen_size(&self) -> usize {
        self.iter().fold(Length.get_gen_size(), |acc, (k, v)| acc + k.get_gen_size() + v.get_gen_size())
    }
}

impl GenSize for ByteArray {
    fn get_gen_size(&self) -> usize {
        Length.get_gen_size() + self.as_slice().get_gen_size()
    }
}

impl GenSize for [u8] {
    fn get_gen_size(&self) -> usize {
        self.len()
    }
}

impl GenSize for AMQPFlags {
    fn get_gen_size(&self) -> usize {
        self.get_bytes().len()
    }
}

impl GenSize for AMQPValue {
    fn get_gen_size(&self) -> usize {
        ShortLength.get_gen_size() + match *self {
            AMQPValue::Boolean(b)         => b.get_gen_size(),
            AMQPValue::ShortShortInt(i)   => i.get_gen_size(),
            AMQPValue::ShortShortUInt(u)  => u.get_gen_size(),
            AMQPValue::ShortInt(i)        => i.get_gen_size(),
            AMQPValue::ShortUInt(u)       => u.get_gen_size(),
            AMQPValue::LongInt(i)         => i.get_gen_size(),
            AMQPValue::LongUInt(u)        => u.get_gen_size(),
            AMQPValue::LongLongInt(i)     => i.get_gen_size(),
            AMQPValue::Float(f)           => f.get_gen_size(),
            AMQPValue::Double(d)          => d.get_gen_size(),
            AMQPValue::DecimalValue(d)    => d.get_gen_size(),
            AMQPValue::ShortString(ref s) => s.get_gen_size(),
            AMQPValue::LongString(ref s)  => s.get_gen_size(),
            AMQPValue::FieldArray(ref a)  => a.get_gen_size(),
            AMQPValue::Timestamp(t)       => t.get_gen_size(),
            AMQPValue::FieldTable(ref t)  => t.get_gen_size(),
            AMQPValue::ByteArray(ref a)   => a.get_gen_size(),
            AMQPValue::Void               => 0,
        }
    }
}
<|endoftext|>


<commit_before>use std::char;

use super::unicode;

const READ_BASES: &[u32] = &[16, 10, 8, 2];

/// Takes a stringly description of a character (the character itself,
/// or a unicode code point name) and returns a Vec of Describable
/// elements that hold the corresponding character. The elements of
/// the vector are sorted by descending numeric code point.
pub fn from_arg(spec: &str) -> Vec<char> {
    let mut chars: Vec<char> = Vec::new();
    let mut try_names = true;

    // match the character itself, or any of its names:
    if spec.chars().count() == 1 {
        if let Some(c) = spec.chars().next() {
            chars.push(c)
        }
        try_names = false;
    } else if spec.starts_with("0x") || spec.starts_with("U+") {
        let _ = u32::from_str_radix(&spec[2..], 16)
            .ok()
            .map(|num| char::from_u32(num).map(|c| chars.push(c)));
    }

    // Match plain numbers in all bases:
    for base in READ_BASES {
        let _ = u32::from_str_radix(spec, *base)
            .ok()
            .map(|num| char::from_u32(num).map(|c| chars.push(c)));
    }

    // Match ^-escapes as control characters
    if spec.len() == 2 && spec.starts_with('^') {
        let control = spec.as_bytes()[1];
        match control {
            0x3f => chars.push(0x7f as char), // ^? is DEL
            _ => chars.push((spec.as_bytes()[1] & 0x1f) as char),
        }
        try_names = false;
    }

    // Match characters from all our name tables:
    if try_names {
        chars.append(unicode::lookup_by_query(spec).as_mut());
    }

    chars.sort_by(|a, b| b.cmp(a));
    chars.dedup();
    chars
}

#[test]
fn from_arg_translates_chars() {
    assert_eq!('n', from_arg("n")[0]);
    assert_eq!(']', from_arg("]")[0]);
}

#[test]
fn from_arg_translates_descriptions() {
    assert_eq!('n', from_arg("latin small letter n")[0]);
    assert_eq!(']', from_arg("right square bracket")[0]);
}

#[test]
fn from_arg_translates_numbers() {
    let received = from_arg("60");
    let mut iter = received.iter();
    assert_eq!('\u{e012b}', *iter.next().unwrap());
    assert_eq!('`', *iter.next().unwrap());
    assert_eq!('<', *iter.next().unwrap());
    assert_eq!('0', *iter.next().unwrap());

    assert_eq!(2, from_arg("0").len());
    assert_eq!(0x30 as char, *from_arg("0").iter().next().unwrap());

    assert_eq!(1, from_arg("0x0").len());
    assert_eq!(1, from_arg("0x41").len());
    assert_eq!('A', from_arg("0x41")[0]);
}

#[test]
fn from_arg_translates_controls() {
    assert_eq!(0x7f as char, from_arg("^?")[0]);
    assert_eq!(0x03 as char, from_arg("^c")[0]);
    assert_eq!(0x03 as char, from_arg("^C")[0]);
}
<commit_msg>Fix a clippy warning, use sort_by_key<commit_after>use std::char;
use std::cmp::Reverse;

use super::unicode;

const READ_BASES: &[u32] = &[16, 10, 8, 2];

/// Takes a stringly description of a character (the character itself,
/// or a unicode code point name) and returns a Vec of Describable
/// elements that hold the corresponding character. The elements of
/// the vector are sorted by descending numeric code point.
pub fn from_arg(spec: &str) -> Vec<char> {
    let mut chars: Vec<char> = Vec::new();
    let mut try_names = true;

    // match the character itself, or any of its names:
    if spec.chars().count() == 1 {
        if let Some(c) = spec.chars().next() {
            chars.push(c)
        }
        try_names = false;
    } else if spec.starts_with("0x") || spec.starts_with("U+") {
        let _ = u32::from_str_radix(&spec[2..], 16)
            .ok()
            .map(|num| char::from_u32(num).map(|c| chars.push(c)));
    }

    // Match plain numbers in all bases:
    for base in READ_BASES {
        let _ = u32::from_str_radix(spec, *base)
            .ok()
            .map(|num| char::from_u32(num).map(|c| chars.push(c)));
    }

    // Match ^-escapes as control characters
    if spec.len() == 2 && spec.starts_with('^') {
        let control = spec.as_bytes()[1];
        match control {
            0x3f => chars.push(0x7f as char), // ^? is DEL
            _ => chars.push((spec.as_bytes()[1] & 0x1f) as char),
        }
        try_names = false;
    }

    // Match characters from all our name tables:
    if try_names {
        chars.append(unicode::lookup_by_query(spec).as_mut());
    }

    chars.sort_by_key(|&k| Reverse(k));
    chars.dedup();
    chars
}

#[test]
fn from_arg_translates_chars() {
    assert_eq!('n', from_arg("n")[0]);
    assert_eq!(']', from_arg("]")[0]);
}

#[test]
fn from_arg_translates_descriptions() {
    assert_eq!('n', from_arg("latin small letter n")[0]);
    assert_eq!(']', from_arg("right square bracket")[0]);
}

#[test]
fn from_arg_translates_numbers() {
    let received = from_arg("60");
    let mut iter = received.iter();
    assert_eq!('\u{e012b}', *iter.next().unwrap());
    assert_eq!('`', *iter.next().unwrap());
    assert_eq!('<', *iter.next().unwrap());
    assert_eq!('0', *iter.next().unwrap());

    assert_eq!(2, from_arg("0").len());
    assert_eq!(0x30 as char, *from_arg("0").iter().next().unwrap());

    assert_eq!(1, from_arg("0x0").len());
    assert_eq!(1, from_arg("0x41").len());
    assert_eq!('A', from_arg("0x41")[0]);
}

#[test]
fn from_arg_translates_controls() {
    assert_eq!(0x7f as char, from_arg("^?")[0]);
    assert_eq!(0x03 as char, from_arg("^c")[0]);
    assert_eq!(0x03 as char, from_arg("^C")[0]);
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::CStr;
use std::mem;

use libc;

use errno::errno;
use crate::error::{Error, Result};
use crate::os::system::Uname;

pub fn uname() -> Result<Uname> {
    unsafe { uname_libc() }
}

unsafe fn uname_libc() -> Result<Uname> {
    let mut utsname: libc::utsname = mem::uninitialized();
    let rv = libc::uname(&mut utsname);
    if rv < 0 {
        let errno = errno();
        let code = errno.0 as i32;
        return Err(Error::UnameFailed(format!(
            "Error {} when calling uname: {}",
            code, errno
        )));
    }
    Ok(Uname {
        sys_name: CStr::from_ptr(utsname.sysname.as_ptr())
            .to_string_lossy()
            .into_owned(),
        node_name: CStr::from_ptr(utsname.nodename.as_ptr())
            .to_string_lossy()
            .into_owned(),
        release: CStr::from_ptr(utsname.release.as_ptr())
            .to_string_lossy()
            .into_owned(),
        version: CStr::from_ptr(utsname.version.as_ptr())
            .to_string_lossy()
            .into_owned(),
        machine: CStr::from_ptr(utsname.machine.as_ptr())
            .to_string_lossy()
            .into_owned(),
    })
}
<commit_msg>rustfmt fixes<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::CStr;
use std::mem;

use libc;

use crate::error::{Error, Result};
use crate::os::system::Uname;
use errno::errno;

pub fn uname() -> Result<Uname> {
    unsafe { uname_libc() }
}

unsafe fn uname_libc() -> Result<Uname> {
    let mut utsname: libc::utsname = mem::uninitialized();
    let rv = libc::uname(&mut utsname);
    if rv < 0 {
        let errno = errno();
        let code = errno.0 as i32;
        return Err(Error::UnameFailed(format!(
            "Error {} when calling uname: {}",
            code, errno
        )));
    }
    Ok(Uname {
        sys_name: CStr::from_ptr(utsname.sysname.as_ptr())
            .to_string_lossy()
            .into_owned(),
        node_name: CStr::from_ptr(utsname.nodename.as_ptr())
            .to_string_lossy()
            .into_owned(),
        release: CStr::from_ptr(utsname.release.as_ptr())
            .to_string_lossy()
            .into_owned(),
        version: CStr::from_ptr(utsname.version.as_ptr())
            .to_string_lossy()
            .into_owned(),
        machine: CStr::from_ptr(utsname.machine.as_ptr())
            .to_string_lossy()
            .into_owned(),
    })
}
<|endoftext|>


<commit_before>use crate::build_utils;
use crate::execution_engine::GenericExecutionEngine;
use crate::genesis_json::geth_genesis_json;
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Output};
use std::{env, fs::File};
use tempfile::TempDir;
use unused_port::unused_tcp_port;

const GETH_BRANCH: &str = "merge-kiln-v2";
const GETH_REPO_URL: &str = "https://github.com/MariusVanDerWijden/go-ethereum";

pub fn build_result(repo_dir: &Path) -> Output {
    Command::new("make")
        .arg("geth")
        .current_dir(&repo_dir)
        .output()
        .expect("failed to make geth")
}

pub fn build(execution_clients_dir: &Path) {
    let repo_dir = execution_clients_dir.join("go-ethereum");

    if !repo_dir.exists() {
        // Clone the repo
        assert!(build_utils::clone_repo(
            execution_clients_dir,
            GETH_REPO_URL
        ));
    }

    // Checkout the correct branch
    assert!(build_utils::checkout_branch(&repo_dir, GETH_BRANCH));

    // Update the branch
    assert!(build_utils::update_branch(&repo_dir, GETH_BRANCH));

    // Build geth
    build_utils::check_command_output(build_result(&repo_dir), "make failed");
}

/*
 * Geth-specific Implementation for GenericExecutionEngine
 */

#[derive(Clone)]
pub struct GethEngine;

impl GethEngine {
    fn binary_path() -> PathBuf {
        let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
        manifest_dir
            .join("execution_clients")
            .join("go-ethereum")
            .join("build")
            .join("bin")
            .join("geth")
    }
}

impl GenericExecutionEngine for GethEngine {
    fn init_datadir() -> TempDir {
        let datadir = TempDir::new().unwrap();

        let genesis_json_path = datadir.path().join("genesis.json");
        let mut file = File::create(&genesis_json_path).unwrap();
        let json = geth_genesis_json();
        serde_json::to_writer(&mut file, &json).unwrap();

        let output = Command::new(Self::binary_path())
            .arg("--datadir")
            .arg(datadir.path().to_str().unwrap())
            .arg("init")
            .arg(genesis_json_path.to_str().unwrap())
            .output()
            .expect("failed to init geth");

        build_utils::check_command_output(output, "geth init failed");

        datadir
    }

    fn start_client(
        datadir: &TempDir,
        http_port: u16,
        http_auth_port: u16,
        jwt_secret_path: PathBuf,
    ) -> Child {
        let network_port = unused_tcp_port().unwrap();

        Command::new(Self::binary_path())
            .arg("--datadir")
            .arg(datadir.path().to_str().unwrap())
            .arg("--http")
            .arg("--http.api")
            .arg("engine,eth")
            .arg("--http.port")
            .arg(http_port.to_string())
            .arg("--authrpc.port")
            .arg(http_auth_port.to_string())
            .arg("--port")
            .arg(network_port.to_string())
            .arg("--authrpc.jwtsecret")
            .arg(jwt_secret_path.as_path().to_str().unwrap())
            .stdout(build_utils::build_stdio())
            .stderr(build_utils::build_stdio())
            .spawn()
            .expect("failed to start geth")
    }
}
<commit_msg>Target geth master in integration testing (#3120)<commit_after>use crate::build_utils;
use crate::execution_engine::GenericExecutionEngine;
use crate::genesis_json::geth_genesis_json;
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Output};
use std::{env, fs::File};
use tempfile::TempDir;
use unused_port::unused_tcp_port;

const GETH_BRANCH: &str = "master";
const GETH_REPO_URL: &str = "https://github.com/ethereum/go-ethereum";

pub fn build_result(repo_dir: &Path) -> Output {
    Command::new("make")
        .arg("geth")
        .current_dir(&repo_dir)
        .output()
        .expect("failed to make geth")
}

pub fn build(execution_clients_dir: &Path) {
    let repo_dir = execution_clients_dir.join("go-ethereum");

    if !repo_dir.exists() {
        // Clone the repo
        assert!(build_utils::clone_repo(
            execution_clients_dir,
            GETH_REPO_URL
        ));
    }

    // Checkout the correct branch
    assert!(build_utils::checkout_branch(&repo_dir, GETH_BRANCH));

    // Update the branch
    assert!(build_utils::update_branch(&repo_dir, GETH_BRANCH));

    // Build geth
    build_utils::check_command_output(build_result(&repo_dir), "make failed");
}

/*
 * Geth-specific Implementation for GenericExecutionEngine
 */

#[derive(Clone)]
pub struct GethEngine;

impl GethEngine {
    fn binary_path() -> PathBuf {
        let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into();
        manifest_dir
            .join("execution_clients")
            .join("go-ethereum")
            .join("build")
            .join("bin")
            .join("geth")
    }
}

impl GenericExecutionEngine for GethEngine {
    fn init_datadir() -> TempDir {
        let datadir = TempDir::new().unwrap();

        let genesis_json_path = datadir.path().join("genesis.json");
        let mut file = File::create(&genesis_json_path).unwrap();
        let json = geth_genesis_json();
        serde_json::to_writer(&mut file, &json).unwrap();

        let output = Command::new(Self::binary_path())
            .arg("--datadir")
            .arg(datadir.path().to_str().unwrap())
            .arg("init")
            .arg(genesis_json_path.to_str().unwrap())
            .output()
            .expect("failed to init geth");

        build_utils::check_command_output(output, "geth init failed");

        datadir
    }

    fn start_client(
        datadir: &TempDir,
        http_port: u16,
        http_auth_port: u16,
        jwt_secret_path: PathBuf,
    ) -> Child {
        let network_port = unused_tcp_port().unwrap();

        Command::new(Self::binary_path())
            .arg("--datadir")
            .arg(datadir.path().to_str().unwrap())
            .arg("--http")
            .arg("--http.api")
            .arg("engine,eth")
            .arg("--http.port")
            .arg(http_port.to_string())
            .arg("--authrpc.port")
            .arg(http_auth_port.to_string())
            .arg("--port")
            .arg(network_port.to_string())
            .arg("--authrpc.jwtsecret")
            .arg(jwt_secret_path.as_path().to_str().unwrap())
            .stdout(build_utils::build_stdio())
            .stderr(build_utils::build_stdio())
            .spawn()
            .expect("failed to start geth")
    }
}
<|endoftext|>


<commit_before>use std::{
    clone::Clone,
    fmt::{self, Debug, Formatter},
    marker::PhantomData,
};

use serde::{
    de::{Deserialize, DeserializeOwned, Deserializer, IgnoredAny, MapAccess, Visitor},
    ser::{Serialize, Serializer},
};
use serde_json::value::RawValue;

use crate::cow::MyCowStr;

/// A wrapper around `Box<RawValue>`, to be used in place of any type in the Matrix endpoint
/// definition to allow request and response types to contain that said type represented by
/// the generic argument `Ev`.
///
/// Ruma offers the `Raw` wrapper to enable passing around JSON text that is only partially
/// validated. This is useful when a client receives events that do not follow the spec perfectly
/// or a server needs to generate reference hashes with the original canonical JSON string.
/// All event structs and enums implement `Serialize` / `Deserialize`, `Raw` should be used
/// to pass around events in a lossless way.
///
/// ```no_run
/// # use serde::Deserialize;
/// # use ruma_serde::Raw;
/// # #[derive(Deserialize)]
/// # struct AnyRoomEvent;
///
/// let json = r#"{ "type": "imagine a full event", "content": {...} }"#;
///
/// let deser = serde_json::from_str::<Raw<AnyRoomEvent>>(json)
///     .unwrap() // the first Result from serde_json::from_str, will not fail
///     .deserialize() // deserialize to the inner type
///     .unwrap(); // finally get to the AnyRoomEvent
/// ```
pub struct Raw<T> {
    json: Box<RawValue>,
    _ev: PhantomData<T>,
}

impl<T> Raw<T> {
    fn new(json: Box<RawValue>) -> Self {
        Self { json, _ev: PhantomData }
    }

    /// Create a `Raw` from a boxed `RawValue`.
    pub fn from_json(raw: Box<RawValue>) -> Self {
        Self::new(raw)
    }

    /// Access the underlying json value.
    pub fn json(&self) -> &RawValue {
        &self.json
    }

    /// Convert `self` into the underlying json value.
    pub fn into_json(self) -> Box<RawValue> {
        self.json
    }

    /// Try to access a given field inside this `Raw`, assuming it contains an
    /// object.
    ///
    /// Returns `Err(_)` when the contained value is not an object, or the field
    /// exists but is fails to deserialize to the expected type.
    ///
    /// Returns `Ok(None)` when the field doesn't exist or is `null`.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # type CustomMatrixEvent = ();
    /// # fn foo() -> serde_json::Result<()> {
    /// # let raw_event: ruma_serde::Raw<()> = todo!();
    /// if raw_event.get_field::<String>("type")?.as_deref() == Some("org.custom.matrix.event") {
    ///     let event: CustomMatrixEvent = serde_json::from_str(raw_event.json().get())?;
    ///     // ...
    /// }
    /// # Ok(())
    /// # }
    /// ```
    pub fn get_field<'a, U>(&'a self, field_name: &str) -> serde_json::Result<Option<U>>
    where
        U: Deserialize<'a>,
    {
        struct SingleFieldVisitor<'b, T> {
            field_name: &'b str,
            _phantom: PhantomData<T>,
        }

        impl<'b, T> SingleFieldVisitor<'b, T> {
            fn new(field_name: &'b str) -> Self {
                Self { field_name, _phantom: PhantomData }
            }
        }

        impl<'b, 'de, T> Visitor<'de> for SingleFieldVisitor<'b, T>
        where
            T: Deserialize<'de>,
        {
            type Value = Option<T>;

            fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("a string")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: MapAccess<'de>,
            {
                let mut res = None;
                while let Some(key) = map.next_key::<MyCowStr<'_>>()? {
                    if key.get() == self.field_name {
                        res = Some(map.next_value()?);
                    } else {
                        map.next_value::<IgnoredAny>()?;
                    }
                }

                Ok(res)
            }
        }

        let mut deserializer = serde_json::Deserializer::from_str(self.json().get());
        deserializer.deserialize_map(SingleFieldVisitor::new(field_name))
    }
}

impl<T> Raw<T>
where
    T: DeserializeOwned,
{
    /// Try to deserialize the JSON into the expected type.
    pub fn deserialize(&self) -> Result<T, serde_json::Error> {
        serde_json::from_str(self.json.get())
    }
}

impl<T: Serialize> From<&T> for Raw<T> {
    fn from(val: &T) -> Self {
        Self::new(serde_json::value::to_raw_value(val).unwrap())
    }
}

// With specialization a fast path from impl for `impl<T> From<Box<RawValue...`
// could be used. Until then there is a special constructor `from_json` for this.
impl<T: Serialize> From<T> for Raw<T> {
    fn from(val: T) -> Self {
        Self::from(&val)
    }
}

impl<T> Clone for Raw<T> {
    fn clone(&self) -> Self {
        Self::new(self.json.clone())
    }
}

impl<T> Debug for Raw<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        use std::any::type_name;
        f.debug_struct(&format!("Raw::<{}>", type_name::<T>())).field("json", &self.json).finish()
    }
}

impl<'de, T> Deserialize<'de> for Raw<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Box::<RawValue>::deserialize(deserializer).map(Self::new)
    }
}

impl<T> Serialize for Raw<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.json.serialize(serializer)
    }
}
<commit_msg>serde: Tiny docs improvement<commit_after>use std::{
    clone::Clone,
    fmt::{self, Debug, Formatter},
    marker::PhantomData,
};

use serde::{
    de::{Deserialize, DeserializeOwned, Deserializer, IgnoredAny, MapAccess, Visitor},
    ser::{Serialize, Serializer},
};
use serde_json::value::RawValue;

use crate::cow::MyCowStr;

/// A wrapper around `Box<RawValue>`, to be used in place of any type in the Matrix endpoint
/// definition to allow request and response types to contain that said type represented by
/// the generic argument `Ev`.
///
/// Ruma offers the `Raw` wrapper to enable passing around JSON text that is only partially
/// validated. This is useful when a client receives events that do not follow the spec perfectly
/// or a server needs to generate reference hashes with the original canonical JSON string.
/// All event structs and enums implement `Serialize` / `Deserialize`, `Raw` should be used
/// to pass around events in a lossless way.
///
/// ```no_run
/// # use serde::Deserialize;
/// # use ruma_serde::Raw;
/// # #[derive(Deserialize)]
/// # struct AnyRoomEvent;
///
/// let json = r#"{ "type": "imagine a full event", "content": {...} }"#;
///
/// let deser = serde_json::from_str::<Raw<AnyRoomEvent>>(json)
///     .unwrap() // the first Result from serde_json::from_str, will not fail
///     .deserialize() // deserialize to the inner type
///     .unwrap(); // finally get to the AnyRoomEvent
/// ```
pub struct Raw<T> {
    json: Box<RawValue>,
    _ev: PhantomData<T>,
}

impl<T> Raw<T> {
    fn new(json: Box<RawValue>) -> Self {
        Self { json, _ev: PhantomData }
    }

    /// Create a `Raw` from a boxed `RawValue`.
    pub fn from_json(raw: Box<RawValue>) -> Self {
        Self::new(raw)
    }

    /// Access the underlying json value.
    pub fn json(&self) -> &RawValue {
        &self.json
    }

    /// Convert `self` into the underlying json value.
    pub fn into_json(self) -> Box<RawValue> {
        self.json
    }

    /// Try to access a given field inside this `Raw`, assuming it contains an
    /// object.
    ///
    /// Returns `Err(_)` when the contained value is not an object, or the field
    /// exists but is fails to deserialize to the expected type.
    ///
    /// Returns `Ok(None)` when the field doesn't exist or is `null`.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # type CustomMatrixEvent = ();
    /// # fn foo() -> serde_json::Result<()> {
    /// # let raw_event: ruma_serde::Raw<()> = todo!();
    /// if raw_event.get_field::<String>("type")?.as_deref() == Some("org.custom.matrix.event") {
    ///     let event: CustomMatrixEvent = serde_json::from_str(raw_event.json().get())?;
    ///     // ...
    /// }
    /// # Ok(())
    /// # }
    /// ```
    pub fn get_field<'a, U>(&'a self, field_name: &str) -> serde_json::Result<Option<U>>
    where
        U: Deserialize<'a>,
    {
        struct SingleFieldVisitor<'b, T> {
            field_name: &'b str,
            _phantom: PhantomData<T>,
        }

        impl<'b, T> SingleFieldVisitor<'b, T> {
            fn new(field_name: &'b str) -> Self {
                Self { field_name, _phantom: PhantomData }
            }
        }

        impl<'b, 'de, T> Visitor<'de> for SingleFieldVisitor<'b, T>
        where
            T: Deserialize<'de>,
        {
            type Value = Option<T>;

            fn expecting(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
                formatter.write_str("a string")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: MapAccess<'de>,
            {
                let mut res = None;
                while let Some(key) = map.next_key::<MyCowStr<'_>>()? {
                    if key.get() == self.field_name {
                        res = Some(map.next_value()?);
                    } else {
                        map.next_value::<IgnoredAny>()?;
                    }
                }

                Ok(res)
            }
        }

        let mut deserializer = serde_json::Deserializer::from_str(self.json().get());
        deserializer.deserialize_map(SingleFieldVisitor::new(field_name))
    }
}

impl<T> Raw<T>
where
    T: DeserializeOwned,
{
    /// Try to deserialize the JSON as the expected type.
    pub fn deserialize(&self) -> Result<T, serde_json::Error> {
        serde_json::from_str(self.json.get())
    }
}

impl<T: Serialize> From<&T> for Raw<T> {
    fn from(val: &T) -> Self {
        Self::new(serde_json::value::to_raw_value(val).unwrap())
    }
}

// With specialization a fast path from impl for `impl<T> From<Box<RawValue...`
// could be used. Until then there is a special constructor `from_json` for this.
impl<T: Serialize> From<T> for Raw<T> {
    fn from(val: T) -> Self {
        Self::from(&val)
    }
}

impl<T> Clone for Raw<T> {
    fn clone(&self) -> Self {
        Self::new(self.json.clone())
    }
}

impl<T> Debug for Raw<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        use std::any::type_name;
        f.debug_struct(&format!("Raw::<{}>", type_name::<T>())).field("json", &self.json).finish()
    }
}

impl<'de, T> Deserialize<'de> for Raw<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Box::<RawValue>::deserialize(deserializer).map(Self::new)
    }
}

impl<T> Serialize for Raw<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.json.serialize(serializer)
    }
}
<|endoftext|>