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
128
129
130
131
132
133
134
135
use futures::Future;
use serde::{Deserialize, Serialize};

use types::HttpMethod;

/// Procedure definition.
pub trait Procedure {
    /// The request type of this procedure.
    type Request: RpcRequest;

    /// The response type of this procedure.
    type Response: RpcResponse;

    /// The HTTP method which this procedure handles.
    fn method() -> HttpMethod;

    /// The entry point of this procedure.
    fn entry_point() -> EntryPoint;
}

/// This trait allows to handle RPC requests issued by clients.
pub trait HandleRpc<P>: Clone + Send + 'static
where
    P: Procedure,
{
    /// The `Future` which represents the result of an invocation of the `handle_rpc` method.
    type Future: Future<Item = <P as Procedure>::Response, Error = NeverFail> + Send + 'static;

    /// Handles an RPC request issued by a client.
    fn handle_rpc(self, request: <P as Procedure>::Request) -> Self::Future;
}

/// A marker type used to indicate that a future never fails.
pub struct NeverFail {
    _cannot_instantiate: (),
}

/// RPC Request.
///
/// Implementations of this trait have to follow conventions as follows.
pub trait RpcRequest: Serialize + for<'a> Deserialize<'a> + Send + 'static {
    /// Returns the body of this HTTP response.
    fn body(&mut self) -> Vec<u8>;

    /// Reads the body of this HTTP response.
    fn read_body(self, body: ::BodyReader) -> ::ReadBody<Self>;
}

/// RPC Response.
///
/// Implementations of this trait have to follow conventions as follows.
pub trait RpcResponse: Serialize + for<'a> Deserialize<'a> {
    /// Returns the body of this HTTP response.
    fn body(&mut self) -> Box<dyn AsRef<[u8]> + Send + 'static>;

    /// Sets the body of this HTTP response.
    fn set_body(&mut self, body: Vec<u8>);
}

/// The entry point definition of a procedure.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EntryPoint {
    segments: &'static [PathSegment],
}
impl EntryPoint {
    /// Makes a new `EntryPoint` instance.
    ///
    /// Usually it is convenient to use `htrpc_entry_point!` macro to construct this.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use]
    /// # extern crate htrpc;
    /// use htrpc::types::{EntryPoint, PathSegment};
    ///
    /// # fn main() {
    /// static SEGMENTS: &[PathSegment] =
    ///     &[PathSegment::Val("foo"), PathSegment::Var, PathSegment::Val("baz")];
    /// let p0 = EntryPoint::new(SEGMENTS);
    /// let p1 = htrpc_entry_point!["foo", _, "baz"];
    /// assert_eq!(p0, p1);
    /// # }
    /// ```
    pub fn new(segments: &'static [PathSegment]) -> Self {
        EntryPoint { segments }
    }

    /// Returns the segments in this entry point.
    pub fn segments(&self) -> &'static [PathSegment] {
        self.segments
    }

    /// Counts variables in this entry point.
    pub fn var_count(&self) -> usize {
        self.segments
            .iter()
            .filter(|s| s == &&PathSegment::Var)
            .count()
    }
}

/// Path segment which is used for constructing `EntryPoint`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PathSegment {
    /// Value (i.e., constant) segment.
    Val(&'static str),

    /// Variable (i.e., wildcard) segment.
    Var,
}
impl PathSegment {
    /// Converts to `Option`.
    pub fn as_option(&self) -> Option<&'static str> {
        if let PathSegment::Val(s) = *self {
            Some(s)
        } else {
            None
        }
    }
}

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

    #[test]
    fn it_works() {
        use self::PathSegment::*;
        static SEGMENTS: &[PathSegment] = &[Val("foo"), Var, Val("baz")];
        let path0 = EntryPoint::new(SEGMENTS);
        let path1 = htrpc_entry_point!["foo", _, "baz"];
        assert_eq!(path0, path1);
    }
}