1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::{error::Error, fmt, str::FromStr, string::ToString};
/// Declares that this endpoint has an order field and can have it set.
///
/// ## Example
///
/// ```
/// use stellar_client::endpoint::{Direction, Order, transaction};
///
/// let txns = transaction::All::default();
/// assert_eq!(txns.order(), None);
///
/// let txns = txns.with_order(Direction::Asc);
/// assert_eq!(txns.order(), Some(Direction::Asc));
///
/// ```
pub trait Order {
    /// Sets the order on the struct and returns an owned version.
    fn with_order(self, order: Direction) -> Self;

    /// Returns the order that has been set, if it has been set.
    fn order(&self) -> Option<Direction>;
}

macro_rules! impl_order {
    ($name:path) => {
        impl Order for $name {
            fn with_order(mut self, order: Direction) -> $name {
                self.order = Some(order);
                self
            }

            fn order(&self) -> Option<Direction> {
                self.order
            }
        }
    };
}

/// The order to return results in.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Direction {
    /// Order the results ascending
    Asc,
    /// Order the results descending
    Desc,
}

impl ToString for Direction {
    fn to_string(&self) -> String {
        match *self {
            Direction::Asc => "asc".to_string(),
            Direction::Desc => "desc".to_string(),
        }
    }
}

/// When a bad token or string is provided to parsing into a direction
/// you get an error.
#[derive(Debug)]
pub struct ParseDirectionError {
    kind: ErrorKind,
}

#[derive(Debug)]
enum ErrorKind {
    InvalidToken,
}

impl Error for ParseDirectionError {
    fn description(&self) -> &str {
        match self.kind {
            ErrorKind::InvalidToken => "Invalid token specified",
        }
    }
}

impl fmt::Display for ParseDirectionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

impl FromStr for Direction {
    type Err = ParseDirectionError;

    fn from_str(s: &str) -> Result<Direction, Self::Err> {
        match s.to_lowercase().as_ref() {
            "asc" => Ok(Direction::Asc),
            "desc" => Ok(Direction::Desc),
            _ => Err(ParseDirectionError {
                kind: ErrorKind::InvalidToken,
            }),
        }
    }
}

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

    #[test]
    fn it_can_become_a_string() {
        assert_eq!(Direction::Asc.to_string(), "asc");
        assert_eq!(Direction::Desc.to_string(), "desc");
    }

    #[test]
    fn it_can_be_derived() {
        struct Foo {
            order: Option<Direction>,
        }
        impl_order!(Foo);

        let foo = Foo { order: None }.with_order(Direction::Asc);
        assert_eq!(foo.order, Some(Direction::Asc));
        assert_eq!(foo.order(), Some(Direction::Asc));
    }

    #[test]
    fn it_can_be_parsed() {
        assert_eq!("asc".parse::<Direction>().unwrap(), Direction::Asc);
        assert_eq!("aSc".parse::<Direction>().unwrap(), Direction::Asc);
        assert_eq!("desc".parse::<Direction>().unwrap(), Direction::Desc);
        assert_eq!("DESC".parse::<Direction>().unwrap(), Direction::Desc);
        assert!("no".parse::<Direction>().is_err());
    }
}