<commit_before>extern crate nom;

use std::str;
use nom::*;

fn is_pn_chars_base(c: char) -> bool {
    let u = c as u32;
    match u {
        0x41    ... 0x5A   | 0x61   ... 0x7A   |
        0xC0    ... 0xD6   | 0xD8   ... 0xF6   |
        0xF8    ... 0x2FF  | 0x370  ... 0x37D  |
        0x37F   ... 0x1FFF | 0x200C ... 0x200D |
        0x2C00  ... 0x2FEF | 0x3001 ... 0xD7FF |
        0xF900  ... 0xFDCF | 0xFDF0 ... 0xFFFD |
        0x10000 ... 0xEFFFF
          => true,
        _ => false,
    }
}

fn is_pn_chars_u(c: char) -> bool {
    is_pn_chars_base(c) || c == '_'
}

fn is_pn_chars(c: char) -> bool {
    let u = c as u32;
    match u {
        0x2d | 0xB7 | 0x30...0x39 | 0x300...0x36F | 0x203F...0x2040
          => true,
        _ => is_pn_chars_base(c),
    }
}

fn is_pn_local_esc(c: char) -> bool {
    match "_~.-!$&'()*+,;=/?#@%".find(c) {
        Some(_) => true,
        _       => false,
    }
}

fn is_ws(c: char) -> bool {
    let u = c as u32;
    u == 0x20 || u == 0x9 || u == 0xD || u == 0xA
}

fn is_anon(c: char) -> bool {
    c == '[' || c == ']' || is_ws(c)
}

fn is_uchar(c: char) -> bool {
    c == '\\' || c == 'u' || is_hex_digit(c as u8)
}

fn is_echar(c: char) -> bool {
    c == '\\' || c == 't' || c == 'b' || c == 'n' || c == 'r' || c == 'f' || c == '"'
}

/* [26] UCHAR */
named!(uchar<&str, &str>, alt!(
    verify!(
        take_s!(6),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'u'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    ) |
    verify!(
        take_s!(10),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'U'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    )
));

/* [159s] ECHAR */
named!(echar<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '\\'
            } else {
                is_echar(c)
            }
        })
    }
));

/* [161s] WS */
named!(ws<&str, &str>, take_while_s!(is_ws));

/* [162s] ANON */
named!(anon<&str, &str>, verify!(
    take_while_s!(is_anon),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '['
            } else if idx == len - 1 {
                c == ']'
            } else {
                is_ws(c)
            }
        })
    }
));

/* [163s] PN_CHARS_BASE */
named!(pn_chars_base<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_base(c)
        }
        false
    }
));

/* [164s] PN_CHARS_U */
named!(pn_chars_u<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_u(c)
        }
        false
    }
));

/* [166s] PN_CHARS */
named!(pn_chars<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars(c)
        }
        false
    }
));

/* [167s] PN_PREFIX */
named!(pn_prefix<&str, &str>, verify!(
    take_until!(":"),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                is_pn_chars_base(c)
            } else if idx == len - 1 {
                c != '.'
            } else {
                is_pn_chars(c) || c == '.'
            }
        })
    }
));

fn is_pn_local(c: char) -> bool {
    is_pn_chars(c) ||
    c == '.'  || c == ':'              ||
    c == '%'  || is_hex_digit(c as u8) ||
    c == '\\' || is_pn_local_esc(c)
}
/* [168s] PN_LOCAL */
named!(pn_local<&str, &str>, verify!(
    take_while_s!(is_pn_local),
    |val:&str| {
        let len = val.char_indices().count();
        let mut percent_count = 0;
        let mut pn_local_esc_count = 0;
        val.char_indices().all(|(idx, c)| {
            if percent_count > 0 {
                percent_count = percent_count - 1;
                is_hex_digit(c as u8)
            } else if c == '%' {
                percent_count = 2;
                true
            } else if pn_local_esc_count > 0 {
                pn_local_esc_count = pn_local_esc_count - 1;
                is_pn_local_esc(c)
            } else if c == '\\' {
                pn_local_esc_count = 1;
                true
            } else if idx == 0 {
                is_pn_chars_u(c) || c == ':'
            } else if idx == len - 1 {
                is_pn_chars(c) || c == ':'
            } else {
                is_pn_chars(c) || c == ':' || c == '.'
            }
        }) && percent_count == 0 && pn_local_esc_count == 0
    }
));

/* [169s] PLX */
named!(plx<&str, &str>, alt!(percent | pn_local_esc));

/* [170s] PERCENT */
named!(percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

/* [172s]  PN_LOCAL_ESC */
named!(pn_local_esc<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let c1 = val.chars().nth(0).unwrap();
        let c2 = val.chars().nth(1).unwrap();
        c1 == '\\' && is_pn_local_esc(c2)
    }
));

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

    #[test]
    fn uchar_test() {
        assert_eq!(uchar("\\u02FFa")    , IResult::Done("a", "\\u02FF")    );
        assert_eq!(uchar("\\U02FFAABBa"), IResult::Done("a", "\\U02FFAABB"));
    }

    #[test]
    fn echar_test() {
        assert_eq!(echar("\\ta"), IResult::Done("a", "\\t"));
    }

    #[test]
    fn ws_test() {
        assert_eq!(ws("   a"), IResult::Done("a", "   "));
    }

    #[test]
    fn anon_test() {
        assert_eq!(anon("[ ]a"), IResult::Done("a", "[ ]"));
    }

    #[test]
    fn pn_chars_base_test() {
        assert_eq!(pn_chars_base("a%2Ab"), IResult::Done("%2Ab", "a"));
    }

    #[test]
    fn pn_chars_u_test() {
        assert_eq!(pn_chars_u("_a"), IResult::Done("a", "_"));
    }

    #[test]
    fn pn_chars_test() {
        assert_eq!(pn_chars("-_a")  , IResult::Done("_a", "-")         );
        assert_eq!(pn_chars(":a2Ab"), IResult::Error(ErrorKind::Verify));
    }

    #[test]
    fn pn_prefix_test() {
        assert_eq!(pn_prefix("hog.e:"), IResult::Done(":", "hog.e")      );
        assert_eq!(pn_prefix("hoge.:"), IResult::Error(ErrorKind::Verify));
    }

    #[test]
    fn pn_local_test() {
        assert_eq!(pn_local("hoge:fuga piyo"), IResult::Done(" piyo", "hoge:fuga"));
        assert_eq!(pn_local("hoge:%28 piyo") , IResult::Done(" piyo", "hoge:%28") );
        assert_eq!(pn_local("hoge:%2 piyo")  , IResult::Error(ErrorKind::Verify)  );
        assert_eq!(pn_local("hoge:%__ piyo") , IResult::Error(ErrorKind::Verify)  );
        assert_eq!(pn_local("hoge:\\* piyo") , IResult::Done(" piyo", "hoge:\\*") );
        assert_eq!(pn_local("%28\\* piyo")   , IResult::Done(" piyo", "%28\\*")   );
        assert_eq!(pn_local("%28\\ piyo")    , IResult::Error(ErrorKind::Verify)  );
    }

    #[test]
    fn plx_test() {
        assert_eq!(plx("%2Abc") , IResult::Done("bc" , "%2A"));
        assert_eq!(plx("\\.%2A"), IResult::Done("%2A", "\\."));
    }

    #[test]
    fn percent_test() {
        assert_eq!(percent("%2Abc"), IResult::Done("bc", "%2A"));
    }

    #[test]
    fn pn_local_esc_test() {
        assert_eq!(pn_local_esc("\\.a b c"), IResult::Done("a b c", "\\."));
    }
}
<commit_msg>Append echar tests<commit_after>extern crate nom;

use std::str;
use nom::*;

fn is_pn_chars_base(c: char) -> bool {
    let u = c as u32;
    match u {
        0x41    ... 0x5A   | 0x61   ... 0x7A   |
        0xC0    ... 0xD6   | 0xD8   ... 0xF6   |
        0xF8    ... 0x2FF  | 0x370  ... 0x37D  |
        0x37F   ... 0x1FFF | 0x200C ... 0x200D |
        0x2C00  ... 0x2FEF | 0x3001 ... 0xD7FF |
        0xF900  ... 0xFDCF | 0xFDF0 ... 0xFFFD |
        0x10000 ... 0xEFFFF
          => true,
        _ => false,
    }
}

fn is_pn_chars_u(c: char) -> bool {
    is_pn_chars_base(c) || c == '_'
}

fn is_pn_chars(c: char) -> bool {
    let u = c as u32;
    match u {
        0x2d | 0xB7 | 0x30...0x39 | 0x300...0x36F | 0x203F...0x2040
          => true,
        _ => is_pn_chars_base(c),
    }
}

fn is_pn_local_esc(c: char) -> bool {
    match "_~.-!$&'()*+,;=/?#@%".find(c) {
        Some(_) => true,
        _       => false,
    }
}

fn is_ws(c: char) -> bool {
    let u = c as u32;
    u == 0x20 || u == 0x9 || u == 0xD || u == 0xA
}

fn is_anon(c: char) -> bool {
    c == '[' || c == ']' || is_ws(c)
}

fn is_uchar(c: char) -> bool {
    c == '\\' || c == 'u' || is_hex_digit(c as u8)
}

fn is_echar(c: char) -> bool {
    c == '\\' || c == 't' || c == 'b' || c == 'n' || c == 'r' || c == 'f' || c == '"' || c == '\''
}

/* [26] UCHAR */
named!(uchar<&str, &str>, alt!(
    verify!(
        take_s!(6),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'u'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    ) |
    verify!(
        take_s!(10),
        |val:&str| {
            let len = val.char_indices().count();
            val.char_indices().all(|(idx, c)| {
                if idx == 0 {
                    c == '\\'
                } else if idx == 1 {
                    c == 'U'
                } else {
                    is_hex_digit(c as u8)
                }
            })
        }
    )
));

/* [159s] ECHAR */
named!(echar<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '\\'
            } else {
                is_echar(c)
            }
        })
    }
));

/* [161s] WS */
named!(ws<&str, &str>, take_while_s!(is_ws));

/* [162s] ANON */
named!(anon<&str, &str>, verify!(
    take_while_s!(is_anon),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                c == '['
            } else if idx == len - 1 {
                c == ']'
            } else {
                is_ws(c)
            }
        })
    }
));

/* [163s] PN_CHARS_BASE */
named!(pn_chars_base<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_base(c)
        }
        false
    }
));

/* [164s] PN_CHARS_U */
named!(pn_chars_u<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars_u(c)
        }
        false
    }
));

/* [166s] PN_CHARS */
named!(pn_chars<&str, &str>, verify!(
    take_s!(1),
    |val:&str| {
        if let Some(c) = val.chars().next() {
            return is_pn_chars(c)
        }
        false
    }
));

/* [167s] PN_PREFIX */
named!(pn_prefix<&str, &str>, verify!(
    take_until!(":"),
    |val:&str| {
        let len = val.char_indices().count();
        val.char_indices().all(|(idx, c)| {
            if idx == 0 {
                is_pn_chars_base(c)
            } else if idx == len - 1 {
                c != '.'
            } else {
                is_pn_chars(c) || c == '.'
            }
        })
    }
));

fn is_pn_local(c: char) -> bool {
    is_pn_chars(c) ||
    c == '.'  || c == ':'              ||
    c == '%'  || is_hex_digit(c as u8) ||
    c == '\\' || is_pn_local_esc(c)
}
/* [168s] PN_LOCAL */
named!(pn_local<&str, &str>, verify!(
    take_while_s!(is_pn_local),
    |val:&str| {
        let len = val.char_indices().count();
        let mut percent_count = 0;
        let mut pn_local_esc_count = 0;
        val.char_indices().all(|(idx, c)| {
            if percent_count > 0 {
                percent_count = percent_count - 1;
                is_hex_digit(c as u8)
            } else if c == '%' {
                percent_count = 2;
                true
            } else if pn_local_esc_count > 0 {
                pn_local_esc_count = pn_local_esc_count - 1;
                is_pn_local_esc(c)
            } else if c == '\\' {
                pn_local_esc_count = 1;
                true
            } else if idx == 0 {
                is_pn_chars_u(c) || c == ':'
            } else if idx == len - 1 {
                is_pn_chars(c) || c == ':'
            } else {
                is_pn_chars(c) || c == ':' || c == '.'
            }
        }) && percent_count == 0 && pn_local_esc_count == 0
    }
));

/* [169s] PLX */
named!(plx<&str, &str>, alt!(percent | pn_local_esc));

/* [170s] PERCENT */
named!(percent<&str, &str>, verify!(
    take_s!(3),
    |val:&str| {
        let bytes = val.as_bytes();
        bytes[0] == 0x25 && is_hex_digit(bytes[1]) && is_hex_digit(bytes[2])
    }
));

/* [172s]  PN_LOCAL_ESC */
named!(pn_local_esc<&str, &str>, verify!(
    take_s!(2),
    |val:&str| {
        let c1 = val.chars().nth(0).unwrap();
        let c2 = val.chars().nth(1).unwrap();
        c1 == '\\' && is_pn_local_esc(c2)
    }
));

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

    #[test]
    fn uchar_test() {
        assert_eq!(uchar("\\u02FFa")    , IResult::Done("a", "\\u02FF")    );
        assert_eq!(uchar("\\U02FFAABBa"), IResult::Done("a", "\\U02FFAABB"));
    }

    #[test]
    fn echar_test() {
        assert_eq!(echar("\\trest") , IResult::Done("rest", "\\t") );
        assert_eq!(echar("\\brest") , IResult::Done("rest", "\\b") );
        assert_eq!(echar("\\nrest") , IResult::Done("rest", "\\n") );
        assert_eq!(echar("\\rrest") , IResult::Done("rest", "\\r") );
        assert_eq!(echar("\\frest") , IResult::Done("rest", "\\f") );
        assert_eq!(echar("\\\"rest"), IResult::Done("rest", "\\\""));
        assert_eq!(echar("\\'rest") , IResult::Done("rest", "\\'") );
        assert_eq!(echar("\\\\rest"), IResult::Done("rest", "\\\\"));
    }

    #[test]
    fn ws_test() {
        assert_eq!(ws("   a"), IResult::Done("a", "   "));
    }

    #[test]
    fn anon_test() {
        assert_eq!(anon("[ ]a"), IResult::Done("a", "[ ]"));
    }

    #[test]
    fn pn_chars_base_test() {
        assert_eq!(pn_chars_base("a%2Ab"), IResult::Done("%2Ab", "a"));
    }

    #[test]
    fn pn_chars_u_test() {
        assert_eq!(pn_chars_u("_a"), IResult::Done("a", "_"));
    }

    #[test]
    fn pn_chars_test() {
        assert_eq!(pn_chars("-_a")  , IResult::Done("_a", "-")         );
        assert_eq!(pn_chars(":a2Ab"), IResult::Error(ErrorKind::Verify));
    }

    #[test]
    fn pn_prefix_test() {
        assert_eq!(pn_prefix("hog.e:"), IResult::Done(":", "hog.e")      );
        assert_eq!(pn_prefix("hoge.:"), IResult::Error(ErrorKind::Verify));
    }

    #[test]
    fn pn_local_test() {
        assert_eq!(pn_local("hoge:fuga piyo"), IResult::Done(" piyo", "hoge:fuga"));
        assert_eq!(pn_local("hoge:%28 piyo") , IResult::Done(" piyo", "hoge:%28") );
        assert_eq!(pn_local("hoge:%2 piyo")  , IResult::Error(ErrorKind::Verify)  );
        assert_eq!(pn_local("hoge:%__ piyo") , IResult::Error(ErrorKind::Verify)  );
        assert_eq!(pn_local("hoge:\\* piyo") , IResult::Done(" piyo", "hoge:\\*") );
        assert_eq!(pn_local("%28\\* piyo")   , IResult::Done(" piyo", "%28\\*")   );
        assert_eq!(pn_local("%28\\ piyo")    , IResult::Error(ErrorKind::Verify)  );
    }

    #[test]
    fn plx_test() {
        assert_eq!(plx("%2Abc") , IResult::Done("bc" , "%2A"));
        assert_eq!(plx("\\.%2A"), IResult::Done("%2A", "\\."));
    }

    #[test]
    fn percent_test() {
        assert_eq!(percent("%2Abc"), IResult::Done("bc", "%2A"));
    }

    #[test]
    fn pn_local_esc_test() {
        assert_eq!(pn_local_esc("\\.a b c"), IResult::Done("a b c", "\\."));
    }
}
<|endoftext|>


<commit_before>//! Crate `retry` provides utilities for retrying operations that can fail.
//!
//! # Usage
//!
//! Retry an operation using the `retry` function. `retry` accepts an iterator over `Duration`s and
//! a closure that returns a `Result` (or `OperationResult`; see below). The iterator is used to
//! determine how long to wait after each unsuccessful try and how many times to try before giving
//! up and returning `Result::Err`. The closure determines either the final successful value, or
//! an error value, which can either be returned immediately or used to indicate that the
//! operation should be retried.
//!
//! Any type that implements `Iterator<Item = Duration>` can be used to determine retry behavior,
//! though a few useful implementations are provided in the `delay` module, including a fixed delay
//! and exponential back-off.
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! The `Iterator` API can be used to limit or modify the delay strategy. For example, to limit the
//! number of retries to 1:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100).take(1), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_err());
//! ```
//!
//! To apply random jitter to any delay strategy, the `jitter` function can be used in combination
//! with the `Iterator` API:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::{Exponential, jitter};
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Exponential::from_millis(10).map(jitter).take(3), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! To deal with fatal errors, return `retry::OperationResult`, which is like std's `Result`, but
//! with a third case to distinguish between errors that should cause a retry and errors that
//! should immediately return, halting retry behavior. (Internally, `OperationResult` is always
//! used, and closures passed to `retry` that return plain `Result` are converted into
//! `OperationResult`.)
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! use retry::OperationResult;
//! let mut collection = vec![1, 2].into_iter();
//! let value = retry(Fixed::from_millis(1), || {
//!     match collection.next() {
//!         Some(n) if n == 2 => OperationResult::Ok(n),
//!         Some(_) => OperationResult::Retry("not 2"),
//!         None => OperationResult::Err("not found"),
//!     }
//! }).unwrap();
//!
//! assert_eq!(value, 2);
//! ```

#![deny(missing_debug_implementations, missing_docs, warnings)]

use std::{
    error::Error as StdError,
    fmt::{Display, Error as FmtError, Formatter},
    thread::sleep,
    time::Duration,
};

pub mod delay;
mod opresult;

#[doc(inline)]
pub use opresult::OperationResult;

/// Retry the given operation synchronously until it succeeds, or until the given `Duration`
/// iterator ends.
pub fn retry<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut() -> OR,
    OR: Into<OperationResult<R, E>>,
{
    retry_with_index(iterable, |_| operation())
}

/// Retry with iteration number. See also [retry](fn.retry.html)
pub fn retry_with_index<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut(u64) -> OR,
    OR: Into<OperationResult<R, E>>,
{
    let mut iterator = iterable.into_iter();
    let mut current_try = 1;
    let mut total_delay = Duration::default();

    loop {
        match operation(current_try).into() {
            OperationResult::Ok(value) => return Ok(value),
            OperationResult::Retry(error) => {
                if let Some(delay) = iterator.next() {
                    sleep(delay);
                    current_try += 1;
                    total_delay += delay;
                } else {
                    return Err(Error::Operation {
                        error,
                        total_delay,
                        tries: current_try,
                    });
                }
            }
            OperationResult::Err(error) => {
                return Err(Error::Operation {
                    error,
                    total_delay,
                    tries: current_try,
                });
            }
        }
    }
}

/// An error with a retryable operation.
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
    /// The operation's last error, plus the number of times the operation was tried and the
    /// duration spent waiting between tries.
    Operation {
        /// The error returned by the operation on the last try.
        error: E,
        /// The duration spent waiting between retries of the operation.
        ///
        /// Note that this does not include the time spent running the operation itself.
        total_delay: Duration,
        /// The total number of times the operation was tried.
        tries: u64,
    },
    /// Something went wrong in the internal logic.
    Internal(String),
}

impl<E> Display for Error<E>
where
    E: StdError,
{
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> {
        write!(formatter, "{}", self.description())
    }
}

impl<E> StdError for Error<E>
where
    E: StdError,
{
    fn description(&self) -> &str {
        match *self {
            Error::Operation { ref error, .. } => error.description(),
            Error::Internal(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match *self {
            Error::Operation { ref error, .. } => Some(error),
            Error::Internal(_) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use super::delay::{Exponential, Fixed, NoDelay, Range};
    use super::opresult::OperationResult;
    use super::{retry, Error};

    #[test]
    fn succeeds_with_infinite_retries() {
        let mut collection = vec![1, 2, 3, 4, 5].into_iter();

        let value = retry(NoDelay, || match collection.next() {
            Some(n) if n == 5 => Ok(n),
            Some(_) => Err("not 5"),
            None => Err("not 5"),
        })
        .unwrap();

        assert_eq!(value, 5);
    }

    #[test]
    fn succeeds_with_maximum_retries() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fails_after_last_try() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "not 2",
                tries: 2,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn fatal_errors() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(2), || match collection.next() {
            Some(n) if n == 2 => OperationResult::Ok(n),
            Some(_) => OperationResult::Err("no retry"),
            None => OperationResult::Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "no retry",
                tries: 1,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn succeeds_with_fixed_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Fixed::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fixed_delay_from_duration() {
        assert_eq!(
            Fixed::from_millis(1_000).next(),
            Fixed::from(Duration::from_secs(1)).next(),
        );
    }

    #[test]
    fn succeeds_with_exponential_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Exponential::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn succeeds_with_ranged_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Range::from_millis_exclusive(1, 10), || {
            match collection.next() {
                Some(n) if n == 2 => Ok(n),
                Some(_) => Err("not 2"),
                None => Err("not 2"),
            }
        })
        .unwrap();

        assert_eq!(value, 2);
    }
}
<commit_msg>Document and test `retry_with_index.`<commit_after>//! Crate `retry` provides utilities for retrying operations that can fail.
//!
//! # Usage
//!
//! Retry an operation using the `retry` function. `retry` accepts an iterator over `Duration`s and
//! a closure that returns a `Result` (or `OperationResult`; see below). The iterator is used to
//! determine how long to wait after each unsuccessful try and how many times to try before giving
//! up and returning `Result::Err`. The closure determines either the final successful value, or
//! an error value, which can either be returned immediately or used to indicate that the
//! operation should be retried.
//!
//! Any type that implements `Iterator<Item = Duration>` can be used to determine retry behavior,
//! though a few useful implementations are provided in the `delay` module, including a fixed delay
//! and exponential back-off.
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! The `Iterator` API can be used to limit or modify the delay strategy. For example, to limit the
//! number of retries to 1:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Fixed::from_millis(100).take(1), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_err());
//! ```
//!
//! To apply random jitter to any delay strategy, the `jitter` function can be used in combination
//! with the `Iterator` API:
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::{Exponential, jitter};
//! let mut collection = vec![1, 2, 3].into_iter();
//!
//! let result = retry(Exponential::from_millis(10).map(jitter).take(3), || {
//!     match collection.next() {
//!         Some(n) if n == 3 => Ok("n is 3!"),
//!         Some(_) => Err("n must be 3!"),
//!         None => Err("n was never 3!"),
//!     }
//! });
//!
//! assert!(result.is_ok());
//! ```
//!
//! To deal with fatal errors, return `retry::OperationResult`, which is like std's `Result`, but
//! with a third case to distinguish between errors that should cause a retry and errors that
//! should immediately return, halting retry behavior. (Internally, `OperationResult` is always
//! used, and closures passed to `retry` that return plain `Result` are converted into
//! `OperationResult`.)
//!
//! ```
//! # use retry::retry;
//! # use retry::delay::Fixed;
//! use retry::OperationResult;
//! let mut collection = vec![1, 2].into_iter();
//! let value = retry(Fixed::from_millis(1), || {
//!     match collection.next() {
//!         Some(n) if n == 2 => OperationResult::Ok(n),
//!         Some(_) => OperationResult::Retry("not 2"),
//!         None => OperationResult::Err("not found"),
//!     }
//! }).unwrap();
//!
//! assert_eq!(value, 2);
//! ```
//!
//! If your operation needs to know how many times it's been tried, use the `retry_with_index`
//! function. This works the same as `retry`, but passes the number of the current try to the
//! closure as an argument.
//!
//! ```
//! # use retry::retry_with_index;
//! # use retry::delay::Fixed;
//! # use retry::OperationResult;
//! let mut collection = vec![1, 2, 3, 4, 5].into_iter();
//!
//! let result = retry_with_index(Fixed::from_millis(100), |current_try| {
//!     if current_try > 3 {
//!         return OperationResult::Err("did not succeed within 3 tries");
//!     }
//!
//!     match collection.next() {
//!         Some(n) if n == 5 => OperationResult::Ok("n is 5!"),
//!         Some(_) => OperationResult::Retry("n must be 5!"),
//!         None => OperationResult::Retry("n was never 5!"),
//!     }
//! });
//!
//! assert!(result.is_err());
//! ```

#![deny(missing_debug_implementations, missing_docs, warnings)]

use std::{
    error::Error as StdError,
    fmt::{Display, Error as FmtError, Formatter},
    thread::sleep,
    time::Duration,
};

pub mod delay;
mod opresult;

#[doc(inline)]
pub use opresult::OperationResult;

/// Retry the given operation synchronously until it succeeds, or until the given `Duration`
/// iterator ends.
pub fn retry<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut() -> OR,
    OR: Into<OperationResult<R, E>>,
{
    retry_with_index(iterable, |_| operation())
}

/// Retry the given operation synchronously until it succeeds, or until the given `Duration`
/// iterator ends, with each iteration of the operation receiving the number of the attempt as an
/// argument.
pub fn retry_with_index<I, O, R, E, OR>(iterable: I, mut operation: O) -> Result<R, Error<E>>
where
    I: IntoIterator<Item = Duration>,
    O: FnMut(u64) -> OR,
    OR: Into<OperationResult<R, E>>,
{
    let mut iterator = iterable.into_iter();
    let mut current_try = 1;
    let mut total_delay = Duration::default();

    loop {
        match operation(current_try).into() {
            OperationResult::Ok(value) => return Ok(value),
            OperationResult::Retry(error) => {
                if let Some(delay) = iterator.next() {
                    sleep(delay);
                    current_try += 1;
                    total_delay += delay;
                } else {
                    return Err(Error::Operation {
                        error,
                        total_delay,
                        tries: current_try,
                    });
                }
            }
            OperationResult::Err(error) => {
                return Err(Error::Operation {
                    error,
                    total_delay,
                    tries: current_try,
                });
            }
        }
    }
}

/// An error with a retryable operation.
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
    /// The operation's last error, plus the number of times the operation was tried and the
    /// duration spent waiting between tries.
    Operation {
        /// The error returned by the operation on the last try.
        error: E,
        /// The duration spent waiting between retries of the operation.
        ///
        /// Note that this does not include the time spent running the operation itself.
        total_delay: Duration,
        /// The total number of times the operation was tried.
        tries: u64,
    },
    /// Something went wrong in the internal logic.
    Internal(String),
}

impl<E> Display for Error<E>
where
    E: StdError,
{
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> {
        write!(formatter, "{}", self.description())
    }
}

impl<E> StdError for Error<E>
where
    E: StdError,
{
    fn description(&self) -> &str {
        match *self {
            Error::Operation { ref error, .. } => error.description(),
            Error::Internal(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&dyn StdError> {
        match *self {
            Error::Operation { ref error, .. } => Some(error),
            Error::Internal(_) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use super::delay::{Exponential, Fixed, NoDelay, Range};
    use super::opresult::OperationResult;
    use super::{retry, retry_with_index, Error};

    #[test]
    fn succeeds_with_infinite_retries() {
        let mut collection = vec![1, 2, 3, 4, 5].into_iter();

        let value = retry(NoDelay, || match collection.next() {
            Some(n) if n == 5 => Ok(n),
            Some(_) => Err("not 5"),
            None => Err("not 5"),
        })
        .unwrap();

        assert_eq!(value, 5);
    }

    #[test]
    fn succeeds_with_maximum_retries() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fails_after_last_try() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "not 2",
                tries: 2,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn fatal_errors() {
        let mut collection = vec![1].into_iter();

        let res = retry(NoDelay.take(2), || match collection.next() {
            Some(n) if n == 2 => OperationResult::Ok(n),
            Some(_) => OperationResult::Err("no retry"),
            None => OperationResult::Err("not 2"),
        });

        assert_eq!(
            res,
            Err(Error::Operation {
                error: "no retry",
                tries: 1,
                total_delay: Duration::from_millis(0)
            })
        );
    }

    #[test]
    fn succeeds_with_fixed_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Fixed::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn fixed_delay_from_duration() {
        assert_eq!(
            Fixed::from_millis(1_000).next(),
            Fixed::from(Duration::from_secs(1)).next(),
        );
    }

    #[test]
    fn succeeds_with_exponential_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Exponential::from_millis(1), || match collection.next() {
            Some(n) if n == 2 => Ok(n),
            Some(_) => Err("not 2"),
            None => Err("not 2"),
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn succeeds_with_ranged_delay() {
        let mut collection = vec![1, 2].into_iter();

        let value = retry(Range::from_millis_exclusive(1, 10), || {
            match collection.next() {
                Some(n) if n == 2 => Ok(n),
                Some(_) => Err("not 2"),
                None => Err("not 2"),
            }
        })
        .unwrap();

        assert_eq!(value, 2);
    }

    #[test]
    fn succeeds_with_index() {
        let mut collection = vec![1, 2, 3].into_iter();

        let value = retry_with_index(NoDelay, |current_try| match collection.next() {
            Some(n) if n == current_try => Ok(n),
            Some(_) => Err("not current_try"),
            None => Err("not current_try"),
        })
        .unwrap();

        assert_eq!(value, 1);
    }
}
<|endoftext|>


<commit_before>//! This is a library for controlling a cursor and a screen,
//! on ANSI terminals.
//!
//!
//! ## Example
//!
//!     use ansi_control::*;
//!

use std::cmp::Ordering;

/// A Pos is position of clearing (display|line) from cursor.
pub enum Pos {
    Back,
    Front,
    Both,
}

/// Moves the cursor _i_ (row), _j_ (column) cells. If the cursor
/// is already at the edge of the screen, this has no effect.
pub fn move_cursor(i: i32, j: i32) -> String {
    let code0 = match i.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}B", -i),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}A", i),
    };
    let code1 = match j.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}D", -j),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}C", j),
    };
    format!("{}{}", code0, code1)
}

/// Moves the cursor to beginning of the line _n_ lines down.
/// If _n_ is a negative number, this function moves the cursor
/// _|n|_ lines up.
pub fn move_line(n: i32) -> String {
    match n.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}F", -n),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}E", n),
    }
}

/// Sets the column of the cursor. (n: column)
pub fn set_column(n: u32) -> String {
    if n == 0 {
        return format!("\x1B[1G")
    }
    format!("\x1B[{}G", n)
}

/// Sets the position of the cursor. (i: row, j: column)
pub fn set_position(i: u32, j: u32) -> String {
    format!("")
}

/// Clears part of screen. If pos is Pos::Back, clear from cursor
/// to the end of the screen. If pos is Pos::Front, clear from
/// cursor to beginning of the screen. If pos is Pos::Both, clear
/// entire screen.
pub fn clear_display(pos: Pos) -> String {
    format!("")
}

/// Clears part of line. If pos is Pos::Back, clear from cursor
/// to the end of the line. If pos is Pos::Front, clear from
/// cursor to beginning of the line. If pos is Pos::Both, clear
/// entire line.
pub fn clear_line(pos: Pos) -> String {
    format!("")
}

/// Scroll whole page up by _n_ lines. If _n_ is a negative
/// number, this function scroll whole page down by _|n|_ lines.
pub fn scroll(n: i32) -> String {
    format!("")
}

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

    macro_rules! test {
        ($name: ident; $ctrl: expr => $result: expr) => {
            #[test]
            fn $name() {
                assert_eq!($result, $ctrl)
            }
        };
    }

    test!(move_cursor0; move_cursor(2, -4) => "\x1B[2A\x1B[4D");
    test!(move_cursor1; move_cursor(-2, 0) => "\x1B[2B");
    test!(move_cursor2; move_cursor(0, 6) => "\x1B[6C");
    test!(move_cursor3; move_cursor(0, 0) => "");

    test!(move_line0; move_line(4) => "\x1B[4E");
    test!(move_line1; move_line(-4) => "\x1B[4F");

    test!(set_column0; set_column(3) => "\x1B[3G");
    test!(set_column1; set_column(0) => "\x1B[1G");

    test!(set_position0; set_position(3, 5) => "\x1B[3;5H");
    test!(set_position1; set_position(0, 0) => "\x1B[1;1H");

    test!(clear_display0; clear_display(Pos::Back) => "\x1B[0J");
    test!(clear_display1; clear_display(Pos::Front) => "\x1B[1J");
    test!(clear_display2; clear_display(Pos::Both) => "\x1B[2J");

    test!(clear_line0; clear_line(Pos::Back) => "\x1B[0K");
    test!(clear_line1; clear_line(Pos::Front) => "\x1B[1K");
    test!(clear_line2; clear_line(Pos::Both) => "\x1B[2K");

    test!(scroll0; scroll(7) => "\x1B[7S");
    test!(scroll1; scroll(-5) => "\x1B[5T");
}
<commit_msg>Improve set_column<commit_after>//! This is a library for controlling a cursor and a screen,
//! on ANSI terminals.
//!
//!
//! ## Example
//!
//!     use ansi_control::*;
//!

use std::cmp::{self, Ordering};

/// A Pos is position of clearing (display|line) from cursor.
pub enum Pos {
    Back,
    Front,
    Both,
}

/// Moves the cursor _i_ (row), _j_ (column) cells. If the cursor
/// is already at the edge of the screen, this has no effect.
pub fn move_cursor(i: i32, j: i32) -> String {
    let code0 = match i.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}B", -i),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}A", i),
    };
    let code1 = match j.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}D", -j),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}C", j),
    };
    format!("{}{}", code0, code1)
}

/// Moves the cursor to beginning of the line _n_ lines down.
/// If _n_ is a negative number, this function moves the cursor
/// _|n|_ lines up.
pub fn move_line(n: i32) -> String {
    match n.cmp(&0) {
        Ordering::Less      => format!("\x1B[{}F", -n),
        Ordering::Equal     => format!(""),
        Ordering::Greater   => format!("\x1B[{}E", n),
    }
}

/// Sets the column of the cursor. (n: column)
pub fn set_column(n: u32) -> String {
    format!("\x1B[{}G", cmp::max(1, n))
}

/// Sets the position of the cursor. (i: row, j: column)
pub fn set_position(i: u32, j: u32) -> String {
    format!("")
}

/// Clears part of screen. If pos is Pos::Back, clear from cursor
/// to the end of the screen. If pos is Pos::Front, clear from
/// cursor to beginning of the screen. If pos is Pos::Both, clear
/// entire screen.
pub fn clear_display(pos: Pos) -> String {
    format!("")
}

/// Clears part of line. If pos is Pos::Back, clear from cursor
/// to the end of the line. If pos is Pos::Front, clear from
/// cursor to beginning of the line. If pos is Pos::Both, clear
/// entire line.
pub fn clear_line(pos: Pos) -> String {
    format!("")
}

/// Scroll whole page up by _n_ lines. If _n_ is a negative
/// number, this function scroll whole page down by _|n|_ lines.
pub fn scroll(n: i32) -> String {
    format!("")
}

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

    macro_rules! test {
        ($name: ident; $ctrl: expr => $result: expr) => {
            #[test]
            fn $name() {
                assert_eq!($result, $ctrl)
            }
        };
    }

    test!(move_cursor0; move_cursor(2, -4) => "\x1B[2A\x1B[4D");
    test!(move_cursor1; move_cursor(-2, 0) => "\x1B[2B");
    test!(move_cursor2; move_cursor(0, 6) => "\x1B[6C");
    test!(move_cursor3; move_cursor(0, 0) => "");

    test!(move_line0; move_line(4) => "\x1B[4E");
    test!(move_line1; move_line(-4) => "\x1B[4F");

    test!(set_column0; set_column(3) => "\x1B[3G");
    test!(set_column1; set_column(0) => "\x1B[1G");

    test!(set_position0; set_position(3, 5) => "\x1B[3;5H");
    test!(set_position1; set_position(0, 0) => "\x1B[1;1H");

    test!(clear_display0; clear_display(Pos::Back) => "\x1B[0J");
    test!(clear_display1; clear_display(Pos::Front) => "\x1B[1J");
    test!(clear_display2; clear_display(Pos::Both) => "\x1B[2J");

    test!(clear_line0; clear_line(Pos::Back) => "\x1B[0K");
    test!(clear_line1; clear_line(Pos::Front) => "\x1B[1K");
    test!(clear_line2; clear_line(Pos::Both) => "\x1B[2K");

    test!(scroll0; scroll(7) => "\x1B[7S");
    test!(scroll1; scroll(-5) => "\x1B[5T");
}
<|endoftext|>


<commit_before>use std::io::{BufReader, IoResult};

struct Triangle {
    normal: [f32, ..3],
    v1: [f32, ..3],
    v2: [f32, ..3],
    v3: [f32, ..3],
    attr_byte_count: u16
}

fn point_eq(lhs: [f32, ..3], rhs: [f32, ..3]) -> bool {
    lhs[0] == rhs[0] && lhs[1] == rhs[1] && lhs[2] == rhs[2]
}

impl PartialEq for Triangle {
    fn eq(&self, rhs: &Triangle) -> bool {
        point_eq(self.normal, rhs.normal)
            && point_eq(self.v1, rhs.v1)
            && point_eq(self.v2, rhs.v2)
            && point_eq(self.v3, rhs.v3)
            && self.attr_byte_count == rhs.attr_byte_count
    }
}

impl Eq for Triangle {}

struct BinaryStlHeader {
    header: [u8, ..80],
    num_triangles: u32
}

pub struct BinaryStlFile {
    header: BinaryStlHeader,
    triangles: Vec<Triangle>
}

fn read_point<T: Reader>(input: &mut T) -> IoResult<[f32, ..3]> {
    let x1 = try!(input.read_le_f32());
    let x2 = try!(input.read_le_f32());
    let x3 = try!(input.read_le_f32());

    Ok([x1, x2, x3])
}

fn read_triangle<T: Reader>(input: &mut T) -> IoResult<Triangle> {
    let normal = try!(read_point(input));
    let v1 = try!(read_point(input));
    let v2 = try!(read_point(input));
    let v3 = try!(read_point(input));
    let attr_count = try!(input.read_le_u16());

    Ok(Triangle { normal: normal,
                  v1: v1, v2: v2, v3: v3,
                  attr_byte_count: attr_count })
}

pub fn read_stl<T: Reader>(input: &mut T) -> IoResult<BinaryStlFile> {
    let mut header = BinaryStlHeader { header: [0u8, ..80],
                                       num_triangles: 0 };

    // read the header
    try!(input.read_at_least(header.header.len(), &mut header.header));

    // TODO: check the header to make sure whether this is a binary
    // STL file.

    header.num_triangles = try!(input.read_le_u32());

    let mut triangles = Vec::new();
    for _ in range(0, header.num_triangles) {
        triangles.push(try!(read_triangle(input)));
    }

    Ok(BinaryStlFile {
        header: header,
        triangles: triangles
    })
}

fn write_point<T: Writer>(out: &mut T, p: [f32, ..3]) -> IoResult<()> {
    for x in p.iter() {
        try!(out.write_le_f32(*x));
    }
    Ok(())
}

pub fn write_stl<T: Writer>(out: &mut T, stl: &BinaryStlFile) -> IoResult<()> {
    assert!(stl.header.num_triangles as uint == stl.triangles.len());

    //write the header.
    try!(out.write(&stl.header.header));
    try!(out.write_le_u32(stl.header.num_triangles));

    // write all the triangles
    for t in stl.triangles.iter() {
        try!(write_point(out, t.normal));
        try!(write_point(out, t.v1));
        try!(write_point(out, t.v2));
        try!(write_point(out, t.v3));
        try!(out.write_le_u16(t.attr_byte_count));
    }

    Ok(())
}

#[test]
fn write_read() {
    // Make sure we can write and read a simple file.
    let file = BinaryStlFile {
        header: BinaryStlHeader { header: [0u8, ..80],
                                  num_triangles: 1 },
        triangles: vec![Triangle { normal: [0f32, 1f32, 0f32],
                                   v1: [0f32, 0f32, 0f32],
                                   v2: [0f32, 0f32, 1f32],
                                   v3: [1f32, 0f32, 1f32],
                                   attr_byte_count: 0 }]
    };

    let mut buffer = Vec::new();

    match write_stl(&mut buffer, &file) {
        Ok(_) => (),
        Err(_) => panic!()
    }

    match read_stl(&mut BufReader::new(buffer.as_slice())) {
        Ok(stl) => {
            assert!(stl.header.num_triangles == file.header.num_triangles);
            assert!(stl.triangles.len() == 1);
            assert!(stl.triangles[0] == file.triangles[0])
        },
        Err(_) => panic!()
    }
}
<commit_msg>Updating to latest rust.<commit_after>use std::io::{BufReader, IoResult};

struct Triangle {
    normal: [f32; 3],
    v1: [f32; 3],
    v2: [f32; 3],
    v3: [f32; 3],
    attr_byte_count: u16
}

fn point_eq(lhs: [f32; 3], rhs: [f32; 3]) -> bool {
    lhs[0] == rhs[0] && lhs[1] == rhs[1] && lhs[2] == rhs[2]
}

impl PartialEq for Triangle {
    fn eq(&self, rhs: &Triangle) -> bool {
        point_eq(self.normal, rhs.normal)
            && point_eq(self.v1, rhs.v1)
            && point_eq(self.v2, rhs.v2)
            && point_eq(self.v3, rhs.v3)
            && self.attr_byte_count == rhs.attr_byte_count
    }
}

impl Eq for Triangle {}

struct BinaryStlHeader {
    header: [u8; 80],
    num_triangles: u32
}

pub struct BinaryStlFile {
    header: BinaryStlHeader,
    triangles: Vec<Triangle>
}

fn read_point<T: Reader>(input: &mut T) -> IoResult<[f32; 3]> {
    let x1 = try!(input.read_le_f32());
    let x2 = try!(input.read_le_f32());
    let x3 = try!(input.read_le_f32());

    Ok([x1, x2, x3])
}

fn read_triangle<T: Reader>(input: &mut T) -> IoResult<Triangle> {
    let normal = try!(read_point(input));
    let v1 = try!(read_point(input));
    let v2 = try!(read_point(input));
    let v3 = try!(read_point(input));
    let attr_count = try!(input.read_le_u16());

    Ok(Triangle { normal: normal,
                  v1: v1, v2: v2, v3: v3,
                  attr_byte_count: attr_count })
}

pub fn read_stl<T: Reader>(input: &mut T) -> IoResult<BinaryStlFile> {
    let mut header = BinaryStlHeader { header: [0u8; 80],
                                       num_triangles: 0 };

    // read the header
    try!(input.read_at_least(header.header.len(), &mut header.header));

    // TODO: check the header to make sure whether this is a binary
    // STL file.

    header.num_triangles = try!(input.read_le_u32());

    let mut triangles = Vec::new();
    for _ in 0 .. header.num_triangles {
        triangles.push(try!(read_triangle(input)));
    }

    Ok(BinaryStlFile {
        header: header,
        triangles: triangles
    })
}

fn write_point<T: Writer>(out: &mut T, p: [f32; 3]) -> IoResult<()> {
    for x in p.iter() {
        try!(out.write_le_f32(*x));
    }
    Ok(())
}

pub fn write_stl<T: Writer>(out: &mut T, stl: &BinaryStlFile) -> IoResult<()> {
    assert!(stl.header.num_triangles as usize == stl.triangles.len());

    //write the header.
    try!(out.write(&stl.header.header));
    try!(out.write_le_u32(stl.header.num_triangles));

    // write all the triangles
    for t in stl.triangles.iter() {
        try!(write_point(out, t.normal));
        try!(write_point(out, t.v1));
        try!(write_point(out, t.v2));
        try!(write_point(out, t.v3));
        try!(out.write_le_u16(t.attr_byte_count));
    }

    Ok(())
}

#[test]
fn write_read() {
    // Make sure we can write and read a simple file.
    let file = BinaryStlFile {
        header: BinaryStlHeader { header: [0u8; 80],
                                  num_triangles: 1 },
        triangles: vec![Triangle { normal: [0f32, 1f32, 0f32],
                                   v1: [0f32, 0f32, 0f32],
                                   v2: [0f32, 0f32, 1f32],
                                   v3: [1f32, 0f32, 1f32],
                                   attr_byte_count: 0 }]
    };

    let mut buffer = Vec::new();

    match write_stl(&mut buffer, &file) {
        Ok(_) => (),
        Err(_) => panic!()
    }

    match read_stl(&mut BufReader::new(&buffer[])) {
        Ok(stl) => {
            assert!(stl.header.num_triangles == file.header.num_triangles);
            assert!(stl.triangles.len() == 1);
            assert!(stl.triangles[0] == file.triangles[0])
        },
        Err(_) => panic!()
    }
}
<|endoftext|>


<commit_before>//! Micro-library for converting from geodetic to UTM coordinates.
#![cfg_attr(feature = "no_std", no_std)]

#[cfg(feature = "no_std")]
extern crate core as std;

use std::f64::consts::PI;

#[cfg(feature = "no_std")]
extern crate num;

pub struct Ellipsoid {
    a: f64,
    f: f64,
}

const WGS84: Ellipsoid = Ellipsoid {
    a: 6378137.0,
    f: 1.0 / 298.257222101,
};

/// Converts a latitude and longitude in decimal degrees to UTM coordinates using the WGS84 ellipsoid.
///
/// # Examples
///
/// ```
/// use utm::to_utm_wgs84;
/// let (northing, easting, meridian_convergence) = to_utm_wgs84(40.62, -123.45, 10);
/// ```
pub fn to_utm_wgs84(latitude: f64, longitude: f64, zone: u8) -> (f64, f64, f64) {
    let latitude = latitude * PI / 180.0;
    let longitude = longitude * PI / 180.0;
    radians_to_utm_wgs84(latitude, longitude, zone)
}

/// Converts a latitude and longitude in radians to UTM coordinates using the WGS84 ellipsoid.
///
/// # Examples
///
/// ```
/// use std::f64::consts::PI;
/// use utm::radians_to_utm_wgs84;
/// let latitude = 40.62 * PI / 180.0;
/// let longitude = -123.45 * PI / 180.0;
/// let (northing, easting, meridian_convergence) = radians_to_utm_wgs84(latitude, longitude, 10);
/// ```
pub fn radians_to_utm_wgs84(latitude: f64, longitude: f64, zone: u8) -> (f64, f64, f64) {
    let ellipsoid = WGS84;
    let long_origin = zone as f64 * 6.0 - 183.0;
    let e2 = 2.0 * ellipsoid.f - ellipsoid.f * ellipsoid.f;
    let ep2 = e2 / (1.0 - e2);

    let n = ellipsoid.a / (1.0 - e2 * latitude.sin() * latitude.sin()).sqrt();
    let t = latitude.tan() * latitude.tan();
    let c = ep2 * latitude.cos() * latitude.cos();
    let a = latitude.cos() * (longitude - (long_origin * PI / 180.0));

    let term1 = 1.0 - e2 / 4.0 - (3.0 * e2 * e2) / 64.0 - (5.0 * e2 * e2 * e2) / 256.0;
    let term2 = (3.0 * e2) / 8.0 + (3.0 * e2 * e2) / 32.0 + (45.0 * e2 * e2 * e2) / 1024.0;
    let term3 = (15.0 * e2 * e2) / 256.0 + (45.0 * e2 * e2 * e2) / 1024.0;
    let term4 = (35.0 * e2 * e2 * e2) / 3072.0;

    let m = ellipsoid.a
        * (term1 * latitude - term2 * (2.0 * latitude).sin() + term3 * (4.0 * latitude).sin()
            - term4 * (6.0 * latitude).sin());

    let x1 = ((1.0 - t + c) * a * a * a) / 6.0;
    let x2 = ((5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ep2) * a * a * a * a * a) / 120.0;
    let x = 0.9996 * n * (a + x1 + x2);

    let y1 = (5.0 - t + 9.0 * c + 4.0 * c * c) * (a * a * a * a) / 24.0;
    let y2 = (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ep2) * (a * a * a * a * a * a) / 720.0;
    let y3 = (a * a) / 2.0 + y1 + y2;
    let y = 0.9996 * (m + n * latitude.tan() * y3);

    let northing = y;
    let easting = x + 500000.0;

    let meridian_convergence = meridian_convergence(northing, easting, WGS84);
    (northing, easting, meridian_convergence)
}

fn meridian_convergence(northing: f64, easting: f64, ellipsoid: Ellipsoid) -> f64 {
    let e2: f64 = 2.0 * ellipsoid.f - ellipsoid.f * ellipsoid.f;
    let e1 = (1.0 - (1.0 - e2).sqrt()) / (1.0 + (1.0 - e2).sqrt());
    let mu_const =
        ellipsoid.a * (1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0);

    let np = northing / 0.9996;
    let mu = np / mu_const;
    let foot_lat = footprint_latitude(e1, mu);

    let ep = (easting - 500000.0) / 0.9996;
    let n = ellipsoid.a / (1.0 - e2 * foot_lat.sin() * foot_lat.sin()).sqrt();
    let m = (ellipsoid.a * (1.0 - e2)) / (1.0 - e2 * foot_lat.sin() * foot_lat.sin()).powf(1.5);

    let conv1 = -(ep / n) * foot_lat.tan();
    let h30 = (ep / n).powi(3);
    let k28 = n / m;
    let k29 = k28 * k28;
    let j29 = foot_lat.tan() * foot_lat.tan();
    let conv2 = (foot_lat.tan() * h30 / 3.0) * (-2.0 * k29 + 3.0 * k28 + j29);
    conv1 + conv2
}

fn footprint_latitude(e1: f64, mu: f64) -> f64 {
    let term1 = 3.0 * e1 / 2.0 - 27.0 * e1 * e1 * e1 / 32.0;
    let term2 = 21.0 * e1 * e1 / 16.0 - 55.0 * e1 * e1 * e1 * e1 / 32.0;
    let term3 = 151.0 * e1 * e1 * e1 / 96.0;
    let term4 = 1097.0 * e1 * e1 * e1 * e1 / 512.0;

    mu + term1 * (2.0 * mu).sin()
        + term2 * (4.0 * mu).sin()
        + term3 * (6.0 * mu).sin()
        + term4 * (8.0 * mu).sin()
}

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

    #[test]
    fn reference() {
        let latitude = 60.9679875497;
        let longitude = -149.119325194;
        let (northing, easting, meridan_convgergence) = to_utm_wgs84(latitude, longitude, 6);
        assert!((385273.02 - easting).abs() < 1e-2);
        assert!((6761077.20 - northing).abs() < 1e-2);
        assert!((0.0323 - meridan_convgergence).abs() < 1e-4);
    }
}
<commit_msg>Rustfmt<commit_after>//! Micro-library for converting from geodetic to UTM coordinates.
#![cfg_attr(feature = "no_std", no_std)]

#[cfg(feature = "no_std")]
extern crate core as std;

use std::f64::consts::PI;

#[cfg(feature = "no_std")]
extern crate num;

pub struct Ellipsoid {
    a: f64,
    f: f64,
}

const WGS84: Ellipsoid = Ellipsoid {
    a: 6378137.0,
    f: 1.0 / 298.257222101,
};

/// Converts a latitude and longitude in decimal degrees to UTM coordinates using the WGS84 ellipsoid.
///
/// # Examples
///
/// ```
/// use utm::to_utm_wgs84;
/// let (northing, easting, meridian_convergence) = to_utm_wgs84(40.62, -123.45, 10);
/// ```
pub fn to_utm_wgs84(latitude: f64, longitude: f64, zone: u8) -> (f64, f64, f64) {
    let latitude = latitude * PI / 180.0;
    let longitude = longitude * PI / 180.0;
    radians_to_utm_wgs84(latitude, longitude, zone)
}

/// Converts a latitude and longitude in radians to UTM coordinates using the WGS84 ellipsoid.
///
/// # Examples
///
/// ```
/// use std::f64::consts::PI;
/// use utm::radians_to_utm_wgs84;
/// let latitude = 40.62 * PI / 180.0;
/// let longitude = -123.45 * PI / 180.0;
/// let (northing, easting, meridian_convergence) = radians_to_utm_wgs84(latitude, longitude, 10);
/// ```
pub fn radians_to_utm_wgs84(latitude: f64, longitude: f64, zone: u8) -> (f64, f64, f64) {
    let ellipsoid = WGS84;
    let long_origin = zone as f64 * 6.0 - 183.0;
    let e2 = 2.0 * ellipsoid.f - ellipsoid.f * ellipsoid.f;
    let ep2 = e2 / (1.0 - e2);

    let n = ellipsoid.a / (1.0 - e2 * latitude.sin() * latitude.sin()).sqrt();
    let t = latitude.tan() * latitude.tan();
    let c = ep2 * latitude.cos() * latitude.cos();
    let a = latitude.cos() * (longitude - (long_origin * PI / 180.0));

    let term1 = 1.0 - e2 / 4.0 - (3.0 * e2 * e2) / 64.0 - (5.0 * e2 * e2 * e2) / 256.0;
    let term2 = (3.0 * e2) / 8.0 + (3.0 * e2 * e2) / 32.0 + (45.0 * e2 * e2 * e2) / 1024.0;
    let term3 = (15.0 * e2 * e2) / 256.0 + (45.0 * e2 * e2 * e2) / 1024.0;
    let term4 = (35.0 * e2 * e2 * e2) / 3072.0;

    let m = ellipsoid.a
        * (term1 * latitude - term2 * (2.0 * latitude).sin() + term3 * (4.0 * latitude).sin()
            - term4 * (6.0 * latitude).sin());

    let x1 = ((1.0 - t + c) * a * a * a) / 6.0;
    let x2 = ((5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * ep2) * a * a * a * a * a) / 120.0;
    let x = 0.9996 * n * (a + x1 + x2);

    let y1 = (5.0 - t + 9.0 * c + 4.0 * c * c) * (a * a * a * a) / 24.0;
    let y2 = (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * ep2) * (a * a * a * a * a * a) / 720.0;
    let y3 = (a * a) / 2.0 + y1 + y2;
    let y = 0.9996 * (m + n * latitude.tan() * y3);

    let northing = y;
    let easting = x + 500000.0;

    let meridian_convergence = meridian_convergence(northing, easting, WGS84);
    (northing, easting, meridian_convergence)
}

fn meridian_convergence(northing: f64, easting: f64, ellipsoid: Ellipsoid) -> f64 {
    let e2: f64 = 2.0 * ellipsoid.f - ellipsoid.f * ellipsoid.f;
    let e1 = (1.0 - (1.0 - e2).sqrt()) / (1.0 + (1.0 - e2).sqrt());
    let mu_const =
        ellipsoid.a * (1.0 - e2 / 4.0 - 3.0 * e2 * e2 / 64.0 - 5.0 * e2 * e2 * e2 / 256.0);

    let np = northing / 0.9996;
    let mu = np / mu_const;
    let foot_lat = footprint_latitude(e1, mu);

    let ep = (easting - 500000.0) / 0.9996;
    let n = ellipsoid.a / (1.0 - e2 * foot_lat.sin() * foot_lat.sin()).sqrt();
    let m = (ellipsoid.a * (1.0 - e2)) / (1.0 - e2 * foot_lat.sin() * foot_lat.sin()).powf(1.5);

    let conv1 = -(ep / n) * foot_lat.tan();
    let h30 = (ep / n).powi(3);
    let k28 = n / m;
    let k29 = k28 * k28;
    let j29 = foot_lat.tan() * foot_lat.tan();
    let conv2 = (foot_lat.tan() * h30 / 3.0) * (-2.0 * k29 + 3.0 * k28 + j29);
    conv1 + conv2
}

fn footprint_latitude(e1: f64, mu: f64) -> f64 {
    let term1 = 3.0 * e1 / 2.0 - 27.0 * e1 * e1 * e1 / 32.0;
    let term2 = 21.0 * e1 * e1 / 16.0 - 55.0 * e1 * e1 * e1 * e1 / 32.0;
    let term3 = 151.0 * e1 * e1 * e1 / 96.0;
    let term4 = 1097.0 * e1 * e1 * e1 * e1 / 512.0;

    mu + term1 * (2.0 * mu).sin()
        + term2 * (4.0 * mu).sin()
        + term3 * (6.0 * mu).sin()
        + term4 * (8.0 * mu).sin()
}

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

    #[test]
    fn reference() {
        let latitude = 60.9679875497;
        let longitude = -149.119325194;
        let (northing, easting, meridan_convgergence) = to_utm_wgs84(latitude, longitude, 6);
        assert!((385273.02 - easting).abs() < 1e-2);
        assert!((6761077.20 - northing).abs() < 1e-2);
        assert!((0.0323 - meridan_convgergence).abs() < 1e-4);
    }
}
<|endoftext|>


<commit_before>#![feature(associated_types)]

use std::ascii::AsciiExt;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::FromTokens;
use types::linestring::LineString;
use types::point::Point;

mod tokenizer;
mod types;

pub enum WktItem {
    Point(Point),
    LineString(LineString),
}

impl WktItem {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens)-> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x: Result<Point, _> = FromTokens::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "LINESTRING" => {
                let x: Result<LineString, _> = FromTokens::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            _ => Err("Invalid type encountered"),
        }
    }
}

pub struct Wkt {
    items: Vec<WktItem>
}

impl Wkt {
    fn new() -> Self {
        Wkt {items: vec![]}
    }

    fn add_item(&mut self, item: WktItem) {
        self.items.push(item);
    }

    fn from_reader(reader: &mut Reader) -> Result<Self, &'static str> {
        match reader.read_to_string() {
            Ok(string) => Wkt::from_str(string.as_slice()),
            Err(err) => Err(err.desc),
        }
    }

    fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.to_ascii_uppercase()
            },
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match WktItem::from_word_and_tokens(word.as_slice(), &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

#[test]
fn empty_string() {
    let wkt = Wkt::from_str("").ok().unwrap();
    assert_eq!(0, wkt.items.len());
}

#[test]
fn basic_point() {
    let mut wkt = Wkt::from_str("POINT (10 -20)").ok().unwrap();
    assert_eq!(1, wkt.items.len());
    let point = match wkt.items.pop().unwrap() {
        WktItem::Point(point) => point,
        _ => unreachable!(),
    };
    assert_eq!(10.0, point.coord.x);
    assert_eq!(-20.0, point.coord.y);
    assert_eq!(None, point.coord.z);
    assert_eq!(None, point.coord.m);
}

#[test]
fn basic_linestring() {
    let mut wkt = Wkt::from_str("LINESTRING (10 -20, -0 -0.5)").ok().unwrap();
    assert_eq!(1, wkt.items.len());
    let linestring = match wkt.items.pop().unwrap() {
        WktItem::LineString(linestring) => linestring,
        _ => unreachable!(),
    };
    assert_eq!(2, linestring.coords.len());

    assert_eq!(10.0, linestring.coords[0].x);
    assert_eq!(-20.0, linestring.coords[0].y);
    assert_eq!(None, linestring.coords[0].z);
    assert_eq!(None, linestring.coords[0].m);

    assert_eq!(0.0, linestring.coords[1].x);
    assert_eq!(-0.5, linestring.coords[1].y);
    assert_eq!(None, linestring.coords[1].z);
    assert_eq!(None, linestring.coords[1].m);
}

#[test]
fn invalid_points() {
    Wkt::from_str("POINT ()").err().unwrap();
    Wkt::from_str("POINT (10)").err().unwrap();
    Wkt::from_str("POINT 10").err().unwrap();
    Wkt::from_str("POINT (10 -20 40)").err().unwrap();
}
<commit_msg>Remove unused method<commit_after>#![feature(associated_types)]

use std::ascii::AsciiExt;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::FromTokens;
use types::linestring::LineString;
use types::point::Point;

mod tokenizer;
mod types;

pub enum WktItem {
    Point(Point),
    LineString(LineString),
}

impl WktItem {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens)-> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x: Result<Point, _> = FromTokens::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            "LINESTRING" => {
                let x: Result<LineString, _> = FromTokens::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            },
            _ => Err("Invalid type encountered"),
        }
    }
}

pub struct Wkt {
    items: Vec<WktItem>
}

impl Wkt {
    fn new() -> Self {
        Wkt {items: vec![]}
    }

    fn add_item(&mut self, item: WktItem) {
        self.items.push(item);
    }

    fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.to_ascii_uppercase()
            },
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match WktItem::from_word_and_tokens(word.as_slice(), &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

#[test]
fn empty_string() {
    let wkt = Wkt::from_str("").ok().unwrap();
    assert_eq!(0, wkt.items.len());
}

#[test]
fn basic_point() {
    let mut wkt = Wkt::from_str("POINT (10 -20)").ok().unwrap();
    assert_eq!(1, wkt.items.len());
    let point = match wkt.items.pop().unwrap() {
        WktItem::Point(point) => point,
        _ => unreachable!(),
    };
    assert_eq!(10.0, point.coord.x);
    assert_eq!(-20.0, point.coord.y);
    assert_eq!(None, point.coord.z);
    assert_eq!(None, point.coord.m);
}

#[test]
fn basic_linestring() {
    let mut wkt = Wkt::from_str("LINESTRING (10 -20, -0 -0.5)").ok().unwrap();
    assert_eq!(1, wkt.items.len());
    let linestring = match wkt.items.pop().unwrap() {
        WktItem::LineString(linestring) => linestring,
        _ => unreachable!(),
    };
    assert_eq!(2, linestring.coords.len());

    assert_eq!(10.0, linestring.coords[0].x);
    assert_eq!(-20.0, linestring.coords[0].y);
    assert_eq!(None, linestring.coords[0].z);
    assert_eq!(None, linestring.coords[0].m);

    assert_eq!(0.0, linestring.coords[1].x);
    assert_eq!(-0.5, linestring.coords[1].y);
    assert_eq!(None, linestring.coords[1].z);
    assert_eq!(None, linestring.coords[1].m);
}

#[test]
fn invalid_points() {
    Wkt::from_str("POINT ()").err().unwrap();
    Wkt::from_str("POINT (10)").err().unwrap();
    Wkt::from_str("POINT 10").err().unwrap();
    Wkt::from_str("POINT (10 -20 40)").err().unwrap();
}
<|endoftext|>


<commit_before>extern crate c_ares_sys;
extern crate libc;

use std::ffi::{CStr, CString};
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::os::unix::io;
use std::ptr;

#[derive(Debug)]
pub enum AresError {
    ENODATA = 1,
    EFORMERR,
    ESERVFAIL,
    ENOTFOUND,
    ENOTIMP,
    EREFUSED,
    EBADQUERY,
    EBADNAME,
    EBADFAMILY,
    EBADRESP,
    ECONNREFUSED,
    ETIMEOUT,
    EOF,
    EFILE,
    ENOMEM,
    EDESTRUCTION,
    EBADSTR,
    EBADFLAGS,
    ENONAME,
    EBADHINTS,
    ENOTINITIALIZED,
    ELOADIPHLPAPI,
    EADDRGETNETWORKPARAMS,
    ECANCELLED,
    UNKNOWN,
}

pub const SOCKET_BAD: io::RawFd = c_ares_sys::ARES_SOCKET_BAD;

pub struct AResult {
    pub ip_addrs: Vec<Ipv4Addr>,
}

pub struct AAAAResult {
    pub ip_addrs: Vec<Ipv6Addr>,
}

pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

impl Channel {
    pub fn new<F>(callback: F) -> Result<Channel, AresError>
        where F: FnOnce(io::RawFd, bool, bool) + 'static {
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        if lib_rc != c_ares_sys::ARES_SUCCESS {
            return Err(ares_error(lib_rc))
        }

        // TODO suport user-provided options
        let mut ares_channel = ptr::null_mut();
        let mut options = c_ares_sys::Struct_ares_options::default();
        options.flags = c_ares_sys::ARES_FLAG_STAYOPEN;
        options.timeout = 500;
        options.tries = 3;
        options.sock_state_cb = Some(socket_callback::<F>);
        options.sock_state_cb_data = unsafe { mem::transmute(Box::new(callback)) };
        let optmask =
            c_ares_sys::ARES_OPT_FLAGS |
            c_ares_sys::ARES_OPT_TIMEOUT |
            c_ares_sys::ARES_OPT_TRIES |
            c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(&mut ares_channel, &mut options, optmask)
        };
        if channel_rc != c_ares_sys::ARES_SUCCESS {
            unsafe { c_ares_sys::ares_library_cleanup(); }
            return Err(ares_error(channel_rc))
        }

        let channel = Channel {
            ares_channel: ares_channel,
        };

        // TODO ares_set_servers() here too?
        Ok(channel)
    }

    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }

    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe { c_ares_sys::ares_process_fd(self.ares_channel, read_fd, write_fd); }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

unsafe impl Send for Channel { }

pub fn str_error<'a>(code: AresError) -> &'a str {
    let buf = unsafe {
        let ptr = c_ares_sys::ares_strerror(code as libc::c_int);
        CStr::from_ptr(ptr).to_bytes()
    };
    std::str::from_utf8(buf).unwrap()
}

// Convert an error code from the library into a more strongly typed AresError.
fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS => AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

#[repr(C)]
struct hostent {
    h_name: *mut libc::c_char,
    h_aliases: *mut *mut libc::c_char,
    h_addrtype: libc::c_int,
    h_length: libc::c_int,
    h_addr_list: *mut *mut libc::c_char,
}

// See arpa/nameser.h
enum QueryType {
    A = 1,
    AAAA = 28,
}

// See arpa/nameser.h
enum DnsClass {
   IN = 1,
}

extern "C" fn socket_callback<F>(
    data: *mut libc::c_void,
    socket_fd: c_ares_sys::ares_socket_t,
    readable: libc::c_int,
    writable: libc::c_int)
    where F: FnOnce(io::RawFd, bool, bool) + 'static {
    let handler: Box<F> = unsafe { mem::transmute(data) };
    handler(socket_fd as io::RawFd, readable != 0, writable != 0);
}

fn parse_a_result(
    status: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int) -> Result<AResult, AresError> {
    if status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(status))
    }

    let mut hostent: *mut hostent = ptr::null_mut();
    let parse_status = unsafe {
        c_ares_sys::ares_parse_a_reply(
            abuf,
            alen,
            &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
            ptr::null_mut(),
            ptr::null_mut())
    };
    if parse_status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(parse_status))
    }

    let mut answers = Vec::new();
    unsafe {
        let mut ptr = (*hostent).h_addr_list;
        while !(*ptr).is_null() {
            let h_addr = *ptr;
            let ip_addr = Ipv4Addr::new(
                *h_addr as u8,
                *h_addr.offset(1) as u8,
                *h_addr.offset(2) as u8,
                *h_addr.offset(3) as u8);
            answers.push(ip_addr);
            ptr = ptr.offset(1);
        }
        c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
    }
    let result = AResult {
        ip_addrs: answers,
    };
    Ok(result)
}

extern "C" fn query_a_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AResult, AresError>) + 'static {
    let result = parse_a_result(status, abuf, alen);
    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

fn parse_aaaa_result(
    status: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int) -> Result<AAAAResult, AresError> {
    if status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(status))
    }

    let mut hostent: *mut hostent = ptr::null_mut();
    let parse_status = unsafe {
        c_ares_sys::ares_parse_aaaa_reply(
            abuf,
            alen,
            &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
            ptr::null_mut(),
            ptr::null_mut())
    };
    if parse_status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(parse_status))
    }

    let mut answers = Vec::new();
    unsafe {
        let mut ptr = (*hostent).h_addr_list;
        while !(*ptr).is_null() {
            let h_addr = *ptr;
            let ip_addr = Ipv6Addr::new(
                ((*h_addr as u16) << 8) + *h_addr.offset(1) as u16,
                ((*h_addr.offset(2) as u16) << 8) + *h_addr.offset(3) as u16,
                ((*h_addr.offset(4) as u16) << 8) + *h_addr.offset(5) as u16,
                ((*h_addr.offset(6) as u16) << 8) + *h_addr.offset(7) as u16,
                ((*h_addr.offset(8) as u16) << 8) + *h_addr.offset(9) as u16,
                ((*h_addr.offset(10) as u16) << 8) + *h_addr.offset(11) as u16,
                ((*h_addr.offset(12) as u16) << 8) + *h_addr.offset(13) as u16,
                ((*h_addr.offset(14) as u16) << 8) + *h_addr.offset(15) as u16);
            answers.push(ip_addr);
            ptr = ptr.offset(1);
        }
        c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
    }
    let result = AAAAResult {
        ip_addrs: answers,
    };
    Ok(result)
}

extern "C" fn query_aaaa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
    let result = parse_aaaa_result(status, abuf, alen);
    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}
<commit_msg>Use ARES_OPT_TIMEOUTMS not ARES_OPT_TIMEOUT<commit_after>extern crate c_ares_sys;
extern crate libc;

use std::ffi::{CStr, CString};
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::os::unix::io;
use std::ptr;

#[derive(Debug)]
pub enum AresError {
    ENODATA = 1,
    EFORMERR,
    ESERVFAIL,
    ENOTFOUND,
    ENOTIMP,
    EREFUSED,
    EBADQUERY,
    EBADNAME,
    EBADFAMILY,
    EBADRESP,
    ECONNREFUSED,
    ETIMEOUT,
    EOF,
    EFILE,
    ENOMEM,
    EDESTRUCTION,
    EBADSTR,
    EBADFLAGS,
    ENONAME,
    EBADHINTS,
    ENOTINITIALIZED,
    ELOADIPHLPAPI,
    EADDRGETNETWORKPARAMS,
    ECANCELLED,
    UNKNOWN,
}

pub const SOCKET_BAD: io::RawFd = c_ares_sys::ARES_SOCKET_BAD;

pub struct AResult {
    pub ip_addrs: Vec<Ipv4Addr>,
}

pub struct AAAAResult {
    pub ip_addrs: Vec<Ipv6Addr>,
}

pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

impl Channel {
    pub fn new<F>(callback: F) -> Result<Channel, AresError>
        where F: FnOnce(io::RawFd, bool, bool) + 'static {
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        if lib_rc != c_ares_sys::ARES_SUCCESS {
            return Err(ares_error(lib_rc))
        }

        // TODO suport user-provided options
        let mut ares_channel = ptr::null_mut();
        let mut options = c_ares_sys::Struct_ares_options::default();
        options.flags = c_ares_sys::ARES_FLAG_STAYOPEN;
        options.timeout = 500;
        options.tries = 3;
        options.sock_state_cb = Some(socket_callback::<F>);
        options.sock_state_cb_data = unsafe { mem::transmute(Box::new(callback)) };
        let optmask =
            c_ares_sys::ARES_OPT_FLAGS |
            c_ares_sys::ARES_OPT_TIMEOUTMS |
            c_ares_sys::ARES_OPT_TRIES |
            c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(&mut ares_channel, &mut options, optmask)
        };
        if channel_rc != c_ares_sys::ARES_SUCCESS {
            unsafe { c_ares_sys::ares_library_cleanup(); }
            return Err(ares_error(channel_rc))
        }

        let channel = Channel {
            ares_channel: ares_channel,
        };

        // TODO ares_set_servers() here too?
        Ok(channel)
    }

    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }

    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe { c_ares_sys::ares_process_fd(self.ares_channel, read_fd, write_fd); }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

unsafe impl Send for Channel { }

pub fn str_error<'a>(code: AresError) -> &'a str {
    let buf = unsafe {
        let ptr = c_ares_sys::ares_strerror(code as libc::c_int);
        CStr::from_ptr(ptr).to_bytes()
    };
    std::str::from_utf8(buf).unwrap()
}

// Convert an error code from the library into a more strongly typed AresError.
fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS => AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

#[repr(C)]
struct hostent {
    h_name: *mut libc::c_char,
    h_aliases: *mut *mut libc::c_char,
    h_addrtype: libc::c_int,
    h_length: libc::c_int,
    h_addr_list: *mut *mut libc::c_char,
}

// See arpa/nameser.h
enum QueryType {
    A = 1,
    AAAA = 28,
}

// See arpa/nameser.h
enum DnsClass {
   IN = 1,
}

extern "C" fn socket_callback<F>(
    data: *mut libc::c_void,
    socket_fd: c_ares_sys::ares_socket_t,
    readable: libc::c_int,
    writable: libc::c_int)
    where F: FnOnce(io::RawFd, bool, bool) + 'static {
    let handler: Box<F> = unsafe { mem::transmute(data) };
    handler(socket_fd as io::RawFd, readable != 0, writable != 0);
}

fn parse_a_result(
    status: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int) -> Result<AResult, AresError> {
    if status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(status))
    }

    let mut hostent: *mut hostent = ptr::null_mut();
    let parse_status = unsafe {
        c_ares_sys::ares_parse_a_reply(
            abuf,
            alen,
            &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
            ptr::null_mut(),
            ptr::null_mut())
    };
    if parse_status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(parse_status))
    }

    let mut answers = Vec::new();
    unsafe {
        let mut ptr = (*hostent).h_addr_list;
        while !(*ptr).is_null() {
            let h_addr = *ptr;
            let ip_addr = Ipv4Addr::new(
                *h_addr as u8,
                *h_addr.offset(1) as u8,
                *h_addr.offset(2) as u8,
                *h_addr.offset(3) as u8);
            answers.push(ip_addr);
            ptr = ptr.offset(1);
        }
        c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
    }
    let result = AResult {
        ip_addrs: answers,
    };
    Ok(result)
}

extern "C" fn query_a_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AResult, AresError>) + 'static {
    let result = parse_a_result(status, abuf, alen);
    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

fn parse_aaaa_result(
    status: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int) -> Result<AAAAResult, AresError> {
    if status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(status))
    }

    let mut hostent: *mut hostent = ptr::null_mut();
    let parse_status = unsafe {
        c_ares_sys::ares_parse_aaaa_reply(
            abuf,
            alen,
            &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
            ptr::null_mut(),
            ptr::null_mut())
    };
    if parse_status != c_ares_sys::ARES_SUCCESS {
        return Err(ares_error(parse_status))
    }

    let mut answers = Vec::new();
    unsafe {
        let mut ptr = (*hostent).h_addr_list;
        while !(*ptr).is_null() {
            let h_addr = *ptr;
            let ip_addr = Ipv6Addr::new(
                ((*h_addr as u16) << 8) + *h_addr.offset(1) as u16,
                ((*h_addr.offset(2) as u16) << 8) + *h_addr.offset(3) as u16,
                ((*h_addr.offset(4) as u16) << 8) + *h_addr.offset(5) as u16,
                ((*h_addr.offset(6) as u16) << 8) + *h_addr.offset(7) as u16,
                ((*h_addr.offset(8) as u16) << 8) + *h_addr.offset(9) as u16,
                ((*h_addr.offset(10) as u16) << 8) + *h_addr.offset(11) as u16,
                ((*h_addr.offset(12) as u16) << 8) + *h_addr.offset(13) as u16,
                ((*h_addr.offset(14) as u16) << 8) + *h_addr.offset(15) as u16);
            answers.push(ip_addr);
            ptr = ptr.offset(1);
        }
        c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
    }
    let result = AAAAResult {
        ip_addrs: answers,
    };
    Ok(result)
}

extern "C" fn query_aaaa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
    let result = parse_aaaa_result(status, abuf, alen);
    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}
<|endoftext|>