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
use std::net::{SocketAddr, ToSocketAddrs};
use axum::Router as AxumRouter;
use crate::internals::*;
use crate::error::*;

pub(crate) mod launch;

/// The main application type.
/// 
/// See the [module-level documentation](crate::app) for more information.
#[derive(Debug)]
pub struct App<State = ()> {
    router: AxumRouter<State>,
    address: Option<SocketAddr>,
    https_address: Option<SocketAddr>,
}

impl<State> App<State> where
    State: Clone + Send + Sync + 'static
{
    /// Creates a new [`App`] instance.
    /// 
    /// This is the main entry point for creating a new application.
    /// 
    /// It is recommended to use the [`App!`] macro instead of this method.
    /// 
    /// [`App`]: crate::App
    /// [`App!`]: macro.App.html
    pub fn new() -> Self {
        Self {
            router: AxumRouter::<State>::new(),
            address: None,
            https_address: None,
        }
    }
    /// Mounts a route handler on the application.
    /// 
    /// This requires a handler that implements the [`AxumHandler`] trait.
    /// Additionally, you need to provide a metadata type that implements the
    /// [`HandlerMetadata`] trait.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// # use catalyzer::*;
    /// # #[main]
    /// # fn main() -> Result {
    /// #[get("/")]
    /// fn index() {
    ///     "Hello, world!"
    /// }
    /// 
    /// let app = App::new()
    ///     .route::<_, index_metadata, _>(index)?;
    /// # }
    /// 
    pub fn route<Return, Meta, Handler>(
        mut self,
        handler: Handler
    ) -> Result<Self> where
        Handler: AxumHandler<Return, State>,
        Meta: HandlerMetadata,
        Return: 'static
    {
        let method_router = match Meta::METHOD {
            Method::GET => axum::routing::get(handler),
            Method::POST => axum::routing::post(handler),
            Method::PUT => axum::routing::put(handler),
            Method::DELETE => axum::routing::delete(handler),
            Method::PATCH => axum::routing::patch(handler),
            Method::HEAD => axum::routing::head(handler),
            Method::OPTIONS => axum::routing::options(handler),
            Method::TRACE => axum::routing::trace(handler),
            _ => return Err(crate::CatalyzerError::UnsupportedMethodError)
        };
        log::trace!("Mounted a {} on \"{}\"", Meta::METHOD, Meta::PATH);
        self.router = self.router.route(Meta::PATH, method_router);
        Ok(self)
    }
    /// Binds the application to a specific address.
    /// 
    /// This is required before launching the application.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// # use catalyzer::*;
    /// # #[main]
    /// # fn main() -> Result {
    /// let app = App::new().bind("0.0.0.0:8080")?;// Localhost on port 8080
    /// # }
    pub fn bind<Addr>(mut self, addr: Addr) -> Result<Self> where
        Addr: ToSocketAddrs
    {
        let mut addrs = addr.to_socket_addrs()?;
        let addr = addrs.next().ok_or(IoError::new(
            IoErrorKind::AddrNotAvailable,
            "No addresses found for the provided address"
        ))?;
        
        log::debug!("Binding to {}", addr);
        self.address = Some(addr);
        Ok(self)
    }
    /// Sets the state of the application.
    /// 
    /// If your application requires a state, you must set it using this method.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// # use catalyzer::*;
    /// struct AppState {
    ///     counter: u32,
    /// }
    /// 
    /// # #[main]
    /// # fn main() -> Result {
    /// let app = App::new()
    ///     .set_state(AppState { counter: 0 });
    /// # }
    pub fn set_state<S2>(self, state: State) -> App<S2> {
        App {
            router: self.router.with_state::<S2>(state),
            address: self.address,
            https_address: self.https_address,
        }
    }
    /// Automatically configures the application.
    /// 
    /// This is only available in debug builds.
    /// 
    /// This is called by the `catalyze!` macro.
    #[doc(hidden)]
    #[cfg(debug_assertions)]
    pub fn __auto_configure(self) -> Result<Self> {
        self.bind("0.0.0.0:3000")
    }
}