1 module scorpion.controller;
2 
3 import lighttp.util : Status, ServerRequest, ServerResponse;
4 
5 import scorpion.context : Context;
6 
7 /**
8  * Attribute for controllers to be used with classes that contain
9  * routes.
10  * Example:
11  * ---
12  * @Controller
13  * @Controller("path")
14  * @Controller("more", "complex", "path")
15  * ---
16  */
17 struct Controller {
18 
19 	string[] path;
20 
21 	this(string[] path...) {
22 		this.path = path;
23 	}
24 
25 }
26 
27 /**
28  * Attributes for routes.
29  * Indicates the method used (case sensitive, usually uppercase),
30  * whether the method can have a body and the path.
31  * Example:
32  * ---
33  * @Route("GET", false, "hello", "world") // GET /hello/world
34  * @Post                                  // POST /
35  * @Delete("resource", "([a-z]+)")        // DELETE /resource/:name
36  * ---
37  */
38 struct Route {
39 
40 	/**
41 	 * Method accepted, conventionally uppercase.
42 	 */
43 	string method;
44 
45 	/**
46 	 * Indicates whether the request can have a body. If set false
47 	 * the body, if present, is always ignored.
48 	 */
49 	bool hasBody;
50 
51 	/**
52 	 * Route's path. It is later glued used path separators by
53 	 * the router manager.
54 	 */
55 	string[] path;
56 
57 	this(string method, bool hasBody, string[] path...) {
58 		this.method = method;
59 		this.hasBody = hasBody;
60 		this.path = path;
61 	}
62 
63 }
64 
65 /// ditto
66 Route Get(string[] path...) {
67 	return Route("GET", false, path);
68 }
69 
70 /// ditto
71 Route Post(string[] path...) {
72 	return Route("POST", true, path);
73 }
74 
75 /// ditto
76 Route Put(string[] path...) {
77 	return Route("PUT", true, path);
78 }
79 
80 /// ditto
81 Route Patch(string[] path...) {
82 	return Route("PATCH", true, path);
83 }
84 
85 /// ditto
86 Route Delete(string[] path...) {
87 	return Route("DELETE", true, path);
88 }
89 
90 /**
91  * Callable functions are routes that can be called from javascipt
92  * using scorpion's javscript file (served to `/assets/scorpion.js`),
93  * calling the `scorpion.call` javscript function.
94  * The javascipt function takes a arguments the name of the function,
95  * an object with the function's parameter and a callback. Both the
96  * object and the callbacks are optional.
97  * Example:
98  * ---
99  * // D code
100  * @Callable
101  * uint randomInteger() {
102  *    return uniform!uint();
103  * }
104  * 
105  * // JS code
106  * scorpion.call("randomInteger", {}, number => console.log(number));
107  * ---
108  * Example:
109  * ---
110  * // D code
111  * @Callable
112  * void startProcess(string name) {
113  *    processFactory.start(name);
114  * }
115  * 
116  * // JS code
117  * scorpion.call("startProcess", {name: "my_process"});
118  * ---
119  * The `Callable` attribute, like other routes, can also be used with
120  * other custom attributes such as `Auth` and `AuthRedirect`.
121  */
122 struct Callable {
123 
124 	/**
125 	 * Optional name of the function. If not present it defaults
126 	 * to the function's name the `Callable` attribute is associated
127 	 * with.
128 	 */
129 	string functionName;
130 
131 }
132 
133 /**
134  * Useful regular expressions for routing.
135  * Note that every regular expression in this enum is enclosed
136  * in a capturing group.
137  */
138 enum Paths : string {
139 
140 	/**
141 	 * Matches a number between 0 and 255.
142 	 */
143 	signedByte = `(1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])`,
144 
145 	/**
146 	 * Matches a number between 0 and 999,999,999.
147 	 */
148 	integer = `([0-9]{1,9})`,
149 
150 	/**
151 	 * Matches a number between 1 and 31.
152 	 */
153 	day = `(3[01]|[12][0-9]|[1-9])`,
154 
155 	/**
156 	 * Matches a number between 1 and 12.
157 	 */
158 	month = `([1-9]|1[0-2])`,
159 
160 }
161 
162 /**
163  * Attribute that indicates that the paramater is from a path's
164  * regex capture.
165  * The number of parameters annotated with `@Path` should correspond
166  * to the number of captures in the path.
167  * The type of the parameter can be any type that can be converted
168  * from a string: it's the programmer's duty to write a regular expression
169  * that won't cause any conversion exception.
170  * Example:
171  * ---
172  * @Get("([a-z]+)")
173  * _(Response response, @Path string capture) {
174  *    ...
175  * }
176  * ---
177  */
178 enum Path;
179 
180 /**
181  * Attribute that indicates that the parameter is from a path's
182  * query.
183  * The parameter's type can be any type that can be converted from
184  * a string. If the conversion fails a `bad request` client error
185  * is returned to the client and the handler is not called.
186  * Example:
187  * ---
188  * @Get("hello")
189  * _(Response response, @Param string username) {
190  *    response.body_ = "Your username: " ~ username;
191  * }
192  * ---
193  */
194 struct Param {
195 
196 	/**
197 	 * Indicates the name of the parameter. If not present defaults
198 	 * to the identifier of the function's parameter that the `Param`
199 	 * attribute is associated to.
200 	 */
201 	string param;
202 
203 }
204 
205 /**
206  * Attribute that indicates that the parameter is converted from
207  * the request's body.
208  * The parameter must be either a struct or a class that will be
209  * insantiated by the validator and validated by it.
210  * The method is not usually called when the validation fails, but
211  * if the method also contains a `scorpion.validation.Validation`
212  * object as paramter the method is called even when the object
213  * was not successfully validated.
214  * To learn more about the validation process and the attributes
215  * that can be added to the object's members to validate see the
216  * `scorpion.validation` module's documentation.
217  * Example:
218  * ---
219  * struct Message {
220  * 
221  *    string sender, message;
222  * 
223  * }
224  * 
225  * @Post("hello")
226  * _(Response response, @Body Message message) {
227  *    response.body_ = "Hello, " ~ message.sender ~ ", thanks for the message.";
228  * }
229  * ---
230  */
231 enum Body;
232 
233 /**
234  * Attribute that marks a function as asynchronous. It means that the
235  * response is not sent to the client when the method returns but when
236  * the `send` function is called in the response.
237  * This attribute should be used when the route's method permorms an
238  * asynchrous action such as a client http call or using the database.
239  * Example:
240  * ---
241  * @Async
242  * @Get
243  * getAsync(Response response) {
244  *    new Client().connect("example.com").get("/").success((ClientResponse cresponse){
245  *       response.body = cresponse.body;
246  *       response.send();
247  *    });
248  * }
249  * ---
250  */
251 struct Async {
252 
253 	bool test(Context context) {
254 		context.response.ready = false;
255 		return true;
256 	}
257 
258 }