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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
use futures::future::{self, FutureObj};
use http_service::HttpService;
use std::{
    any::Any,
    fmt::Debug,
    ops::{Deref, DerefMut},
    sync::Arc,
};

use crate::{
    configuration::{Configuration, Store},
    endpoint::BoxedEndpoint,
    endpoint::Endpoint,
    extract::Extract,
    middleware::{logger::RootLogger, RequestContext},
    router::{EndpointData, Resource, RouteResult, Router},
    Middleware, Request, Response, RouteMatch,
};

/// The top-level type for setting up a Tide application.
///
/// Apps are equipped with a handle to their own state (`Data`), which is available to all endpoints.
/// This is a "handle" because it must be `Clone`, and endpoints are invoked with a fresh clone.
/// They also hold a top-level router.
///
/// # Examples
///
/// You can start a simple Tide application that listens for `GET` requests at path `/hello`
/// on `127.0.0.1:8181` with:
///
/// ```rust, no_run
/// #![feature(async_await)]
///
/// let mut app = tide::App::new(());
/// app.at("/hello").get(async || "Hello, world!");
/// app.serve()
/// ```
///
/// `App` state can be modeled with an underlying `Data` handle for a cloneable type `T`. Endpoints
/// can receive a fresh clone of that handle (in addition to data extracted from the request) by
/// defining a parameter of type `AppData<T>`:
///
/// ```rust, no_run
/// #![feature(async_await, futures_api)]
///
/// use std::sync::Arc;
/// use std::sync::Mutex;
/// use tide::AppData;
/// use tide::body;
///
/// #[derive(Clone, Default)]
/// struct Database {
///     contents: Arc<Mutex<Vec<String>>>,
/// }
///
/// async fn insert(
///     mut db: AppData<Database>,
///     msg: body::Str,
/// ) -> String {
///     // insert into db
///     # String::from("")
/// }
///
/// fn main() {
///     let mut app = tide::App::new(Database::default());
///     app.at("/messages/insert").post(insert);
///     app.serve()
/// }
/// ```
///
/// Where to go from here: Please see [`Router`](struct.Router.html) and [`Endpoint`](trait.Endpoint.html)
/// for further examples.
///
pub struct App<Data> {
    data: Data,
    router: Router<Data>,
    default_handler: EndpointData<Data>,
}

impl<Data: Clone + Send + Sync + 'static> App<Data> {
    /// Set up a new app with some initial `data`.
    pub fn new(data: Data) -> App<Data> {
        let logger = RootLogger::new();
        let mut app = App {
            data,
            router: Router::new(),
            default_handler: EndpointData {
                endpoint: BoxedEndpoint::new(async || http::status::StatusCode::NOT_FOUND),
                store: Store::new(),
            },
        };

        // Add RootLogger as a default middleware
        app.middleware(logger);
        app.setup_configuration();

        app
    }

    // Add default configuration
    fn setup_configuration(&mut self) {
        let config = Configuration::build().finalize();
        self.config(config);
    }

    /// Get the top-level router.
    pub fn router(&mut self) -> &mut Router<Data> {
        &mut self.router
    }

    /// Add a new resource at `path`.
    /// See [Router.at](struct.Router.html#method.at) for details.
    pub fn at<'a>(&'a mut self, path: &'a str) -> Resource<'a, Data> {
        self.router.at(path)
    }

    /// Set the default handler for the app, a fallback function when there is no match to the route requested
    pub fn default_handler<T: Endpoint<Data, U>, U>(
        &mut self,
        handler: T,
    ) -> &mut EndpointData<Data> {
        let endpoint = EndpointData {
            endpoint: BoxedEndpoint::new(handler),
            store: self.router.store_base.clone(),
        };
        self.default_handler = endpoint;
        &mut self.default_handler
    }

    /// Apply `middleware` to the whole app. Note that the order of nesting subrouters and applying
    /// middleware matters; see `Router` for details.
    pub fn middleware(&mut self, middleware: impl Middleware<Data> + 'static) -> &mut Self {
        self.router.middleware(middleware);
        self
    }

    /// Add a default configuration `item` for the whole app.
    pub fn config<T: Any + Debug + Clone + Send + Sync>(&mut self, item: T) -> &mut Self {
        self.router.config(item);
        self
    }

    pub fn get_item<T: Any + Debug + Clone + Send + Sync>(&self) -> Option<&T> {
        self.router.get_item()
    }

    /// Make this app into an `HttpService`.
    pub fn into_http_service(mut self) -> Server<Data> {
        self.router.apply_default_config();
        Server {
            data: self.data,
            router: Arc::new(self.router),
            default_handler: Arc::new(self.default_handler),
        }
    }

    /// Start serving the app at the given address.
    ///
    /// Blocks the calling thread indefinitely.
    #[cfg(feature = "hyper")]
    pub fn serve(self) {
        let configuration = self.get_item::<Configuration>().unwrap();
        let addr = format!("{}:{}", configuration.address, configuration.port)
            .parse::<std::net::SocketAddr>()
            .unwrap();

        println!("Server is listening on: http://{}", addr);

        crate::serve::serve(self.into_http_service(), addr);
    }
}

#[derive(Clone)]
pub struct Server<Data> {
    data: Data,
    router: Arc<Router<Data>>,
    default_handler: Arc<EndpointData<Data>>,
}

impl<Data> HttpService for Server<Data>
where
    Data: Clone + Send + Sync + 'static,
{
    type Connection = ();
    type ConnectionFuture = future::Ready<Result<(), std::io::Error>>;
    type Fut = FutureObj<'static, Result<http_service::Response, std::io::Error>>;

    fn connect(&self) -> Self::ConnectionFuture {
        future::ok(())
    }

    fn respond(&self, _conn: &mut (), req: http_service::Request) -> Self::Fut {
        let data = self.data.clone();
        let router = self.router.clone();
        let default_handler = self.default_handler.clone();
        let path = req.uri().path().to_owned();
        let method = req.method().to_owned();

        FutureObj::new(Box::new(
            async move {
                let RouteResult {
                    endpoint,
                    params,
                    middleware,
                } = router.route(&path, &method, &default_handler);

                let ctx = RequestContext {
                    app_data: data,
                    req,
                    params,
                    endpoint,
                    next_middleware: middleware,
                };
                Ok(await!(ctx.next()))
            },
        ))
    }
}

/// An extractor for accessing app data.
///
/// Endpoints can use `AppData<T>` to gain a handle to the data (of type `T`) originally injected into their app.
pub struct AppData<T>(pub T);

impl<T> Deref for AppData<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}
impl<T> DerefMut for AppData<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T: Clone + Send + 'static> Extract<T> for AppData<T> {
    type Fut = future::Ready<Result<Self, Response>>;
    fn extract(
        data: &mut T,
        req: &mut Request,
        params: &Option<RouteMatch<'_>>,
        store: &Store,
    ) -> Self::Fut {
        future::ok(AppData(data.clone()))
    }
}