swim_core/
lib.rs

1//! The `swim_core` crate provides the core structures and traits for building a Swim application.
2
3pub mod app;
4pub mod error;
5pub mod http;
6pub mod log;
7pub mod macros;
8pub mod middleware;
9pub mod model;
10pub mod project;
11pub mod route;
12pub mod settings;
13pub mod sugar;
14pub mod view;
15
16use std::{
17    net::{Ipv4Addr, SocketAddr},
18    str::FromStr,
19    sync::Arc,
20};
21
22use routerify::{Router, RouterService};
23
24// Re-exports
25pub use crate::{
26    app::{App, AppConfig},
27    error::Error,
28    http::{Body, Request, Response, StatusCode},
29    middleware::{Logger, Middleware},
30    model::Model,
31    project::Project,
32    route::Route,
33    settings::{CoreSettings, DatabaseSettings, Settings},
34    view::View,
35};
36
37// Re-export external crates for convenience
38pub use async_trait;
39pub use hyper;
40pub use routerify;
41
42/// Convinience Result type for the `swim` crate.
43///
44/// This is just a type alias for `std::result::Result<T, Error>`. Where `T` is the type of the value that is returned on success and `Error` is the swim error type.
45pub type Result<T> = std::result::Result<T, Error>;
46
47/// The `Swim` struct is the main entry point for a Swim application.
48///
49/// It is created using the `swim!` macro, or by using the builder (or in this case; `with`) pattern.
50#[derive(Debug)]
51pub struct Swim {
52    project: Box<dyn Project>,
53    host: String,
54    port: u16,
55}
56
57impl Swim {
58    /// Creates a new `Swim` instance using the given `Project`.
59    pub fn with(project: Box<dyn Project>) -> Self {
60        Self {
61            project,
62            host: "127.0.0.1".to_string(),
63            port: 8000,
64        }
65    }
66
67    /// Mutates the `host` field of the `Swim` instance. This is the hostname or IP address that the server will listen on.
68    ///
69    /// The default value is `127.0.0.1`.
70    pub fn host(mut self, host: &str) -> Self {
71        self.host = host.to_string();
72        self
73    }
74
75    /// Mutates the `port` field of the `Swim` instance. This is the port that the server will listen on.
76    ///
77    /// The default value is `8000`.
78    pub fn port(mut self, port: u16) -> Self {
79        self.port = port;
80        self
81    }
82
83    /// Starts the server and runs the application.
84    ///
85    /// This method is `async`, and will block until the server is stopped.
86    pub async fn swim(self) -> Result<()> {
87        // Parse the host and port
88        let ip_address = match self.host {
89            host if host == "localhost" => Ipv4Addr::LOCALHOST,
90            host => Ipv4Addr::from_str(&host)?,
91        };
92        let address = SocketAddr::from((ip_address, self.port));
93
94        let _settings = self.project.settings();
95        let apps = self.project.apps();
96
97        // Create a vector of middlewares, with default middlewares
98        // This is done so that default middlewares take precedence over user-defined middlewares
99        let mut middlewares: Vec<Box<dyn Middleware>> = vec![Box::new(Logger)];
100
101        // User-defined middlewares
102        middlewares.extend(self.project.middlewares());
103
104        // Print some sweet project details (inspired by Rocket :D)
105        project_details!(&self.project, &address);
106        for app in apps.iter() {
107            app_details!(app);
108        }
109        if !middlewares.is_empty() {
110            middleware_listing!(middlewares);
111        }
112
113        // Returns a router
114        let get_router = || {
115            let mut builder = Router::<Body, Error>::builder();
116
117            for app in apps.iter() {
118                let mut scoped_router_builder = Router::builder();
119
120                for route in app.routes() {
121                    let view_get = route.view.clone();
122                    let view_post = route.view.clone();
123                    let view_put = route.view.clone();
124                    let view_patch = route.view.clone();
125                    let view_delete = route.view.clone();
126
127                    // Add binding for GET method
128                    scoped_router_builder = scoped_router_builder.get(&route.path, move |req| {
129                        let view = view_get.clone();
130                        async move { view.get(req).await }
131                    });
132
133                    // Add binding for POST method
134                    scoped_router_builder = scoped_router_builder.post(&route.path, move |req| {
135                        let view = view_post.clone();
136                        async move { view.post(req).await }
137                    });
138
139                    // Add binding for PUT method
140                    scoped_router_builder = scoped_router_builder.put(&route.path, move |req| {
141                        let view = view_put.clone();
142                        async move { view.put(req).await }
143                    });
144
145                    // Add binding for PATCH method
146                    scoped_router_builder = scoped_router_builder.patch(&route.path, move |req| {
147                        let view = view_patch.clone();
148                        async move { view.patch(req).await }
149                    });
150
151                    // Add binding for DELETE method
152                    scoped_router_builder = scoped_router_builder.delete(&route.path, move |req| {
153                        let view = view_delete.clone();
154                        async move { view.delete(req).await }
155                    });
156                }
157
158                builder = builder.scope(app.mount(), scoped_router_builder.build().unwrap());
159            }
160
161            // Middlewares
162            for middleware in middlewares.into_iter() {
163                let middleware = Arc::new(middleware);
164
165                // Pre middleware
166                let middleware_pre = middleware.clone();
167                builder = builder.middleware(routerify::Middleware::pre(move |req| {
168                    let middleware_cloned = middleware_pre.clone();
169
170                    async move { middleware_cloned.pre(req).await }
171                }));
172
173                // Post middleware
174                let middleware_post = middleware.clone();
175                builder = builder.middleware(routerify::Middleware::post(move |res| {
176                    let middleware_cloned = middleware_post.clone();
177
178                    async move { middleware_cloned.post(res).await }
179                }));
180            }
181
182            builder.build().unwrap()
183        };
184
185        // Report the server address to the user.
186        launch_info!(address);
187
188        // Make a service to handle each connection.
189        let service = RouterService::new(get_router()).unwrap();
190
191        // Bind and serve the service.
192        hyper::server::Server::try_bind(&address)?
193            .serve(service)
194            .await?;
195
196        Ok(())
197    }
198}