afast/lib.rs
1//! # AFast
2//!
3//! **AFast** is a high-performance asynchronous Rust backend framework designed
4//! to simplify building networked applications. It supports multiple protocols
5//! via feature flags and provides automatic code generation for clients
6//! (TypeScript and JavaScript), API documentation, and field validation.
7//!
8//! ## Instructions
9//!
10//! ### Supported Protocol Features
11//!
12//! You can enable the following features in your `Cargo.toml`:
13//!
14//! - `http` - enable HTTP support
15//! - `/` - Document path (feature flag `doc`)
16//! - `/api` - HTTP API endpoints
17//! - `/code/{service}/{lang}` - Client code (feature flag `js`|`ts` ...)
18//! - `/doc` - Service list (feature flag `doc`)
19//! - `/doc/{service}` - Handler defintions and documentation (feature flag `doc`)
20//! - `ws` - enable WebSocket support
21//! - `/ws` - WebSocket endpoint
22//! - `tcp` - enable TCP support
23//! - `doc` - enable API documentation generation
24//! - `js` - enable JavaScript client generation (auto enabled `code`)
25//! - `ts` - enable TypeScript client generation (auto enabled `code`)
26//! - `code` - enable code generation
27//!
28//! **Note on TCP usage:**
29//!
30//! If the `tcp` feature is enabled, the `AFast::serve` method takes two arguments:
31//!
32//! 1. The TCP address to listen on (`"127.0.0.1:8080"`).
33//! 2. The HTTP/WS address (`"127.0.0.1:8081"`) for web clients and generated JS/TS clients.
34//!
35//! This allows you to run TCP and HTTP/WS servers simultaneously in the same application.
36//!
37//! ### Key Features
38//!
39//! - **`handler` Macro**: Declare HTTP endpoints with minimal boilerplate
40//! - Automatic TypeScript/JavaScript client generation
41//! - Namespace support for organized API structure (`ns("api.v1.user")`)
42//! - Descriptive API documentation generation (`desc("Get user info")`)
43//! - Automatic field validation with custom rules
44//! - Async handler functions with state management
45//! - Flexible multi-protocol support: HTTP, WS, TCP
46//!
47//! #### Handler Macro Overview
48//!
49//! The `#[handler]` attribute macro transforms async functions into full-featured API endpoints:
50//!
51//! ```rust
52//! #[handler(desc("Get user information"), ns("api.v1.user"))]
53//! async fn get_user(state: String, header: Header, req: Request) -> Result<Response, Error> {
54//! // Your business logic
55//! }
56//! ```
57//!
58//! **Macro Parameters:**
59//!
60//! - `desc("description")` - API description for documentation
61//! - `ns("api.v1.user")` - Namespace for nested JS client generation
62//!
63//! **Generated Output:**
64//!
65//! - Type-safe HTTP endpoints
66//! - Nested JavaScript client structure
67//! - TypeScript type definitions
68//! - OpenAPI documentation
69//!
70//! ### Upcoming Features / Development Plan
71//!
72//! - Nested structure validation for complex types
73//! - Enable or disable js / ts / document by feature flags
74//! - Add command for generating client code
75//! - Generate client code for additional languages: Java, Kotlin, C#, Rust, etc.
76//! - Improved code generation templates for easier integration
77//! - Enhanced error handling and validation reporting
78//!
79//! ## Example
80//!
81//! ```rust
82//! use afast::{AFast, AFastData, AFastKind, Error, handler, middleware, register};
83//!
84//! #[derive(Debug, Clone, AFastData, AFastKind)]
85//! enum Sex {
86//! Other,
87//! Custom(#[validate(desc("Custom user sex 0"))] i32, String),
88//! Male {
89//! #[validate(desc("Male user id"))]
90//! id: i64,
91//! },
92//! Female {
93//! #[validate(desc("Female user name"))]
94//! name: String,
95//! },
96//! }
97//!
98//! #[derive(Debug, Clone, AFastData, AFastKind)]
99//! struct Request {
100//! #[validate(desc("User ID"))]
101//! id: i64,
102//! #[validate(desc("User name"))]
103//! name: String,
104//! #[validate(
105//! desc("User age"),
106//! required("age is required"),
107//! min(1, "age must be at least 1"),
108//! max(256, "age must be at most 256")
109//! )]
110//! age: u32,
111//! #[validate(desc("User hobbies"))]
112//! hobbies: Vec<Hobby>,
113//! #[validate(desc("User tags"))]
114//! tags: Vec<String>,
115//! #[validate(desc("User gender"))]
116//! gender: Option<bool>,
117//! #[validate(desc("User sex"))]
118//! sex: Sex,
119//! }
120//!
121//! #[derive(Debug, Clone, AFastData, AFastKind)]
122//! struct Hobby {
123//! id: i64,
124//! name: String,
125//! }
126//!
127//! #[derive(Debug, AFastData, AFastKind)]
128//! pub struct Response {
129//! sex: Sex,
130//! id: i64,
131//! name: String,
132//! age: u32,
133//! hobbies: Vec<Hobby>,
134//! tags: Vec<String>,
135//! gender: Option<bool>,
136//! }
137//!
138//! #[handler(desc("Get user information"), ns("api.user"))]
139//! async fn get_user(_state: String, _header: Header, req: Request) -> Result<Response, Error> {
140//! Ok(Response {
141//! id: req.id,
142//! name: req.name.clone(),
143//! age: req.age,
144//! hobbies: req.hobbies.clone(),
145//! tags: req.tags.clone(),
146//! gender: req.gender,
147//! sex: req.sex.clone(),
148//! })
149//! }
150//!
151//! #[derive(Debug, AFastData, AFastKind)]
152//! struct Req2 {
153//! id: i64,
154//! }
155//!
156//! #[derive(Debug, AFastData, AFastKind)]
157//! struct Resp2 {
158//! id: i64,
159//! name: String,
160//! }
161//!
162//! #[handler(desc("Get user by id"), ns("api"))]
163//! async fn get_id(_state: String, _header: Header, req: Req2) -> Result<Resp2, Error> {
164//! Ok(Resp2 {
165//! id: req.id,
166//! name: "John".to_string(),
167//! })
168//! }
169//!
170//! #[derive(Debug, Clone, AFastData, AFastKind)]
171//! struct Header {
172//! token: String,
173//! }
174//!
175//! #[middleware]
176//! async fn auth(_state: String, header: Header) -> Result<Header, Error> {
177//! println!("Token: {:?}", header);
178//! Ok(header)
179//! }
180//!
181//! #[tokio::main]
182//! async fn main() {
183//! let state = "".to_string();
184//!
185//! let server = AFast::<String, Header>::new(state)
186//! .service("user", "User service", register! { get_user, get_id })
187//! .middleware(auth);
188//!
189//! server
190//! .serve(
191//! #[cfg(feature = "tcp")]
192//! &"127.0.0.1:8080",
193//! #[cfg(any(feature = "http", feature = "ws"))]
194//! &"127.0.0.1:8081",
195//! )
196//! .await
197//! .unwrap();
198//! }
199//! ```
200//!
201
202use std::sync::Arc;
203
204pub use afast_macros::*;
205use tokio::net::ToSocketAddrs;
206
207#[cfg(feature = "doc")]
208mod doc;
209mod error;
210#[cfg(feature = "js")]
211mod js;
212#[cfg(feature = "ts")]
213mod ts;
214pub use error::Error;
215
216pub trait AFastData: Sized {
217 fn to_bytes(&self) -> Vec<u8>;
218 fn from_bytes(buf: &[u8]) -> Result<(Self, usize), Error>;
219 fn validate(&self) -> Result<(), Vec<&'static str>>;
220}
221
222/// The type of a handler function.
223///
224/// Each handler receives shared state `Arc<T>` and a binary request slice `&[u8]`,
225/// and returns a Future that resolves to a binary response `Vec<u8>` or an `Error`.
226pub type Handler<T, H> = dyn Fn(
227 T,
228 H,
229 &[u8],
230 ) -> std::pin::Pin<
231 Box<dyn std::future::Future<Output = Result<Vec<u8>, Error>> + Send + 'static>,
232 > + Send
233 + Sync;
234
235pub type Middleware<T, H> = dyn Fn(
236 T,
237 H,
238 )
239 -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<H, Error>> + Send + 'static>>
240 + Send
241 + Sync;
242
243/// Generic handler struct that wraps a user-defined handler function.
244///
245/// Contains a handler ID, name, JS request code, TS request code, and the processing function.
246pub struct HandlerGeneric<T, H>
247where
248 T: Clone + Send + Sync + 'static,
249 H: AFastKind + AFastData,
250{
251 /// Unique handler ID
252 pub id: u32,
253 /// Handler name
254 pub name: &'static str,
255
256 /// JS client
257 #[cfg(feature = "js")]
258 pub js: String,
259
260 /// TS client
261 #[cfg(feature = "ts")]
262 pub ts: String,
263
264 /// API documentation
265 #[cfg(feature = "doc")]
266 pub doc: String,
267
268 /// Namespace for this handler
269 pub namespace: Vec<String>,
270 /// Actual processing function
271 pub func: Box<Handler<T, H>>,
272}
273
274/// Core AFast service struct.
275///
276/// Maintains shared state and registered handlers.
277pub struct AFast<T, H>
278where
279 T: Clone + Send + Sync + 'static,
280 H: AFastKind + AFastData + Send + Sync + 'static,
281{
282 /// Shared application state
283 state: T,
284 /// Middleware function
285 pub middleware: Arc<Box<Middleware<T, H>>>,
286 /// Registered handler list
287 handlers: Arc<Vec<HandlerGeneric<T, H>>>,
288
289 /// Client code
290 #[cfg(feature = "code")]
291 codes: std::collections::HashMap<String, String>,
292
293 services: std::collections::HashSet<(String, String, usize)>,
294
295 /// Includs JS util
296 #[cfg(feature = "js")]
297 js_util: bool,
298
299 /// Includs TS util
300 #[cfg(feature = "ts")]
301 ts_util: bool,
302}
303
304impl<T, H> AFast<T, H>
305where
306 T: Clone + Send + Sync + 'static,
307 H: AFastKind + AFastData + Send + Sync + 'static,
308{
309 /// Create a new AFast service instance
310 ///
311 /// # Arguments
312 /// * `state` - Shared application state
313 /// * `handlers` - List of registered handlers
314 ///
315 /// # Returns
316 /// A new AFast instance
317 pub fn new(state: T) -> Self {
318 Self {
319 state,
320 middleware: Arc::new(Box::new(|_state, header| Box::pin(async { Ok(header) }))),
321 handlers: Arc::new(vec![]),
322 #[cfg(feature = "code")]
323 codes: std::collections::HashMap::new(),
324 services: std::collections::HashSet::new(),
325 #[cfg(feature = "js")]
326 js_util: true,
327 #[cfg(feature = "ts")]
328 ts_util: true,
329 }
330 }
331
332 pub fn middleware(mut self, middleware: fn() -> Box<Middleware<T, H>>) -> Self {
333 self.middleware = Arc::new(middleware());
334 self
335 }
336
337 #[cfg(feature = "js")]
338 /// Enable or disable JS util
339 /// The service will take effect after this
340 pub fn disable_js_util(mut self) -> Self {
341 self.js_util = false;
342 self
343 }
344
345 #[cfg(feature = "ts")]
346 /// Enable or disable TS util
347 /// The service will take effect after this
348 pub fn disable_ts_util(mut self) -> Self {
349 self.ts_util = false;
350 self
351 }
352
353 /// Register a service with additional handlers
354 pub fn service(mut self, name: &str, desc: &str, handlers: Vec<HandlerGeneric<T, H>>) -> Self {
355 if handlers.is_empty() {
356 println!("No handlers found for service {}", name);
357 return self;
358 }
359
360 if self.services.iter().any(|s| s.0 == name) {
361 panic!("Service {} already registered", name);
362 }
363
364 self.services
365 .insert((name.to_string(), desc.to_string(), handlers.len()));
366
367 #[cfg(feature = "js")]
368 self.codes.insert(
369 format!("code/{}/js", name),
370 js::gen_js_code(self.js_util, self.handlers.len(), &handlers),
371 );
372
373 #[cfg(feature = "ts")]
374 self.codes.insert(
375 format!("code/{}/ts", name),
376 ts::gen_ts_code(self.js_util, self.handlers.len(), &handlers),
377 );
378
379 #[cfg(feature = "doc")]
380 self.codes.insert(
381 format!("doc/{}", name),
382 doc::gen_doc(self.handlers.len(), &handlers),
383 );
384
385 let existing_handlers = Arc::get_mut(&mut self.handlers).unwrap();
386 existing_handlers.extend(handlers);
387 self.handlers = Arc::clone(&self.handlers);
388
389 self
390 }
391
392 /// Start the server
393 ///
394 /// Depending on feature flags, this will start TCP, HTTP, and/or WebSocket services.
395 ///
396 /// # Arguments
397 /// * `addr_tcp` - TCP listening address (only active if `tcp` feature enabled)
398 /// * `addr_http` - HTTP/WebSocket listening address (only active if `http` or `ws` feature enabled)
399 ///
400 /// # Returns
401 /// Result<(), Error>
402 pub async fn serve<A: ToSocketAddrs>(
403 &self,
404 #[cfg(feature = "tcp")] addr_tcp: A,
405 #[cfg(any(feature = "http", feature = "ws"))] addr_http: A,
406 ) -> Result<(), Error> {
407 #[cfg(feature = "tcp")]
408 {
409 // Clone shared state and handler list for TCP processing
410 let state = self.state.clone();
411 let handlers = Arc::clone(&self.handlers);
412
413 // Bind TCP listener
414 let listener = tokio::net::TcpListener::bind(addr_tcp).await.unwrap();
415
416 #[cfg(any(feature = "http", feature = "ws"))]
417 {
418 // Spawn TCP listener task if HTTP/WS is also enabled
419 let mw = Arc::clone(&self.middleware);
420 tokio::spawn(async move {
421 loop {
422 let (mut socket, _) = listener.accept().await.unwrap();
423
424 // Read 4-byte length prefix
425 let mut body = [0u8; 4];
426 tokio::io::AsyncReadExt::read_exact(&mut socket, &mut body)
427 .await
428 .unwrap();
429 let len = u32::from_be_bytes(body) as usize;
430
431 // Read message body
432 let mut body = vec![0u8; len];
433 tokio::io::AsyncReadExt::read_exact(&mut socket, &mut body)
434 .await
435 .unwrap();
436
437 let (header, size) = match H::from_bytes(&body) {
438 Ok(h) => h,
439 Err(_) => {
440 continue;
441 }
442 };
443
444 // Parse sequence number and handler ID
445 let seq: usize = u32::from_be_bytes([
446 body[size + 0],
447 body[size + 1],
448 body[size + 2],
449 body[size + 3],
450 ]) as usize;
451 let id: usize = u32::from_be_bytes([
452 body[size + 4],
453 body[size + 5],
454 body[size + 6],
455 body[size + 7],
456 ]) as usize;
457
458 // Call handler
459 let handler = &handlers[id];
460
461 let fut = mw(state.clone(), header);
462 let header = match fut.await {
463 Ok(r) => r,
464 Err(_) => {
465 continue;
466 }
467 };
468 let h = header.to_bytes();
469 let fut = (handler.func)(state.clone(), header, &body[size + 8..]);
470 let res = fut.await.unwrap();
471
472 // Build response: len + seq + id + response
473 let mut final_res = Vec::with_capacity(12 + res.len() + h.len());
474 final_res.extend_from_slice(&final_res.len().to_be_bytes());
475 final_res.extend_from_slice(&seq.to_be_bytes());
476 final_res.extend_from_slice(&id.to_be_bytes());
477 final_res.extend_from_slice(&h);
478 final_res.extend_from_slice(&res);
479
480 tokio::io::AsyncWriteExt::write_all(&mut socket, &final_res)
481 .await
482 .unwrap();
483 }
484 });
485 }
486
487 #[cfg(not(any(feature = "http", feature = "ws")))]
488 {
489 // Blocking TCP loop if HTTP/WS is disabled
490 let mw = Arc::clone(&self.middleware);
491 loop {
492 let (mut socket, _) = listener.accept().await.unwrap();
493
494 // Read 4-byte length prefix
495 let mut body = [0u8; 4];
496 tokio::io::AsyncReadExt::read_exact(&mut socket, &mut body)
497 .await
498 .unwrap();
499 let len = u32::from_be_bytes(body) as usize;
500
501 // Read message body
502 let mut body = vec![0u8; len];
503 tokio::io::AsyncReadExt::read_exact(&mut socket, &mut body)
504 .await
505 .unwrap();
506
507 let (header, size) = match H::from_bytes(&body) {
508 Ok(h) => h,
509 Err(_) => {
510 continue;
511 }
512 };
513
514 // Parse sequence number and handler ID
515 let seq: usize = u32::from_be_bytes([
516 body[size + 0],
517 body[size + 1],
518 body[size + 2],
519 body[size + 3],
520 ]) as usize;
521 let id: usize = u32::from_be_bytes([
522 body[size + 4],
523 body[size + 5],
524 body[size + 6],
525 body[size + 7],
526 ]) as usize;
527
528 // Call handler
529 let handler = &handlers[id];
530
531 let fut = mw(state.clone(), header);
532 let header = match fut.await {
533 Ok(r) => r,
534 Err(_) => {
535 continue;
536 }
537 };
538 let h = header.to_bytes();
539 let fut = (handler.func)(state.clone(), header, &body[size + 8..]);
540 let res = fut.await.unwrap();
541
542 // Build response: len + seq + id + response
543 let mut final_res = Vec::with_capacity(12 + res.len() + h.len());
544 final_res.extend_from_slice(&final_res.len().to_be_bytes());
545 final_res.extend_from_slice(&seq.to_be_bytes());
546 final_res.extend_from_slice(&id.to_be_bytes());
547 final_res.extend_from_slice(&h);
548 final_res.extend_from_slice(&res);
549
550 tokio::io::AsyncWriteExt::write_all(&mut socket, &final_res)
551 .await
552 .unwrap();
553 }
554 }
555 }
556
557 #[cfg(any(feature = "http", feature = "ws", feature = "code", feature = "doc"))]
558 {
559 #[cfg(feature = "code")]
560 let codes = Arc::new(self.codes.clone());
561
562 #[cfg(any(feature = "http", feature = "ws"))]
563 let state = self.state.clone();
564
565 #[cfg(any(feature = "http", feature = "ws"))]
566 let listener = tokio::net::TcpListener::bind(addr_http).await.unwrap();
567 loop {
568 let (stream, _) = listener.accept().await.unwrap();
569 let io = hyper_util::rt::TokioIo::new(stream);
570 let state = state.clone();
571 let mw = Arc::clone(&self.middleware);
572 let handlers = Arc::clone(&self.handlers);
573
574 #[cfg(feature = "code")]
575 let codes = Arc::clone(&codes);
576 #[cfg(feature = "doc")]
577 let services = Arc::new(self.services.clone());
578
579 tokio::spawn(async move {
580 let state = state.clone();
581 let mw = Arc::clone(&mw);
582 let handlers = Arc::clone(&handlers);
583 #[cfg(feature = "code")]
584 let codes = Arc::clone(&codes);
585 #[cfg(feature = "doc")]
586 let services = Arc::clone(&services);
587 if let Err(err) = hyper::server::conn::http1::Builder::new(
588 // hyper_util::rt::TokioExecutor::new(),
589 )
590 .serve_connection(
591 io,
592 hyper::service::service_fn(|req: hyper::Request<_>| {
593 let state = state.clone();
594 let mw = Arc::clone(&mw);
595 let handlers = Arc::clone(&handlers);
596 #[cfg(feature = "code")]
597 let codes = Arc::clone(&codes);
598 #[cfg(feature = "doc")]
599 let services = Arc::clone(&services);
600
601 async move {
602 let path = req.uri().path();
603 let method = req.method();
604 #[cfg(feature = "ws")]
605 let upgrade = req
606 .headers()
607 .get("Upgrade")
608 .map(|v| v == "websocket")
609 .unwrap_or(false);
610 #[cfg(feature = "ws")]
611 let sec_ws_key = req.headers().contains_key("Sec-WebSocket-Key");
612 #[cfg(feature = "ws")]
613 if path == "/ws"
614 && method == hyper::Method::GET
615 && upgrade
616 && sec_ws_key
617 {
618 let upgraded = hyper::upgrade::on(req).await.unwrap();
619 let stream = hyper_util::rt::TokioIo::new(upgraded);
620 println!("WebSocket client connected!");
621
622 let mut ws =
623 tokio_tungstenite::WebSocketStream::from_raw_socket(
624 stream,
625 tokio_tungstenite::tungstenite::protocol::Role::Server,
626 None,
627 )
628 .await;
629
630 while let Some(msg) =
631 futures_util::StreamExt::next(&mut ws).await
632 {
633 let msg = msg.unwrap();
634
635 match msg {
636 tokio_tungstenite::tungstenite::Message::Binary(
637 body,
638 ) => {
639 if body.len() < 8 {
640 continue;
641 }
642
643 let (header, size) = match H::from_bytes(&body) {
644 Ok(h) => h,
645 Err(_) => {
646 continue;
647 }
648 };
649
650 let seq: usize = u32::from_be_bytes([
651 body[size + 0],
652 body[size + 1],
653 body[size + 2],
654 body[size + 3],
655 ])
656 as usize;
657 let id: usize = u32::from_be_bytes([
658 body[size + 4],
659 body[size + 5],
660 body[size + 6],
661 body[size + 7],
662 ])
663 as usize;
664
665 let handler = &handlers[id];
666
667 let fut = mw(state.clone(), header);
668 let header = match fut.await {
669 Ok(r) => r,
670 Err(_) => {
671 continue;
672 }
673 };
674 let h = header.to_bytes();
675 let fut = (handler.func)(
676 state.clone(),
677 header,
678 &body[size + 8..],
679 );
680 let res = fut.await.unwrap();
681 let mut final_res =
682 Vec::with_capacity(8 + res.len() + h.len());
683 final_res.extend_from_slice(&seq.to_be_bytes());
684 final_res.extend_from_slice(&id.to_be_bytes());
685 final_res.extend_from_slice(&h);
686 final_res.extend_from_slice(&res);
687 futures_util::SinkExt::send(
688 &mut ws,
689 tokio_tungstenite::tungstenite::Message::Binary(
690 final_res.into(),
691 ),
692 )
693 .await
694 .unwrap();
695 }
696 tokio_tungstenite::tungstenite::Message::Close(_) => {
697 println!("Client disconnected");
698 break;
699 }
700 _ => {}
701 }
702 }
703
704 return Ok::<_, std::convert::Infallible>(
705 hyper::Response::builder()
706 .status(400)
707 .body(http_body_util::Full::<hyper::body::Bytes>::new(
708 "".into(),
709 ))
710 .unwrap(),
711 );
712 }
713
714 #[cfg(feature = "http")]
715 if method == hyper::Method::POST && path == "/api" {
716 let body = http_body_util::BodyExt::collect(req)
717 .await
718 .unwrap()
719 .to_bytes();
720 let (header, size) = match H::from_bytes(&body[..]) {
721 Ok(h) => h,
722 Err(e) => {
723 return Ok::<_, std::convert::Infallible>(
724 hyper::Response::builder()
725 .status(400)
726 .body(http_body_util::Full::<hyper::body::Bytes>::new(
727 e.to_string().into(),
728 ))
729 .unwrap(),
730 );
731 }
732 };
733
734 if body.len() < size + 4 {
735 return Ok::<_, std::convert::Infallible>(
736 hyper::Response::builder()
737 .status(400)
738 .body(http_body_util::Full::<hyper::body::Bytes>::new(
739 "Invalid request".into(),
740 ))
741 .unwrap(),
742 );
743 }
744
745 let id = u32::from_be_bytes([
746 body[size + 0],
747 body[size + 1],
748 body[size + 2],
749 body[size + 3],
750 ]);
751 let handler = &handlers[id as usize];
752
753 let fut = mw(state.clone(), header);
754 let header = match fut.await {
755 Ok(r) => r,
756 Err(e) => {
757 return Ok::<_, std::convert::Infallible>(
758 hyper::Response::builder()
759 .status(400)
760 .body(http_body_util::Full::<hyper::body::Bytes>::new(
761 e.to_string().into(),
762 ))
763 .unwrap(),
764 );
765 }
766 };
767 let h = header.to_bytes();
768 let fut =
769 (handler.func)(state.clone(), header, &body[size + 4..]);
770 match fut.await {
771 Ok(res) => {
772 let mut final_res =
773 Vec::with_capacity(4 + res.len() + h.len());
774 final_res.extend_from_slice(&id.to_be_bytes());
775 final_res.extend_from_slice(&h);
776 final_res.extend_from_slice(&res);
777 return Ok::<_, std::convert::Infallible>(
778 hyper::Response::builder()
779 .status(200)
780 .body(http_body_util::Full::<hyper::body::Bytes>::new(
781 final_res.into(),
782 ))
783 .unwrap(),
784 );
785 }
786 Err(e) => {
787 let (c, m) = match e {
788 Error::DecodeError => {
789 (400, "Invalid request".to_string())
790 }
791 Error::EncodeError => {
792 (500, "Invalid response".to_string())
793 }
794 Error::ClientError => {
795 (400, "Bad request".to_string())
796 }
797 Error::ServerError => {
798 (500, "Internal Server Error".to_string())
799 }
800 Error::CustomError(c, m) => (c, m.to_string()),
801 };
802 return Ok::<_, std::convert::Infallible>(
803 hyper::Response::builder()
804 .status(c)
805 .body(http_body_util::Full::<hyper::body::Bytes>::new(m.into()))
806 .unwrap(),
807 );
808 }
809 }
810 }
811
812 #[cfg(any(feature = "code", feature = "doc"))]
813 if method == hyper::Method::GET {
814 #[cfg(feature = "code")]
815 if path.starts_with("/code") {
816 let parts: Vec<&str> = path
817 .trim_start_matches('/')
818 .trim_end_matches('/')
819 .split('/')
820 .collect();
821 let len = parts.len();
822 if len != 3 {
823 return Ok::<_, std::convert::Infallible>(
824 hyper::Response::builder()
825 .status(404)
826 .body(http_body_util::Full::<hyper::body::Bytes>::new(
827 "404 Not Found".into(),
828 ))
829 .unwrap(),
830 );
831 }
832 let (service, lang) = (parts[1], parts[2]);
833 let code = if let Some(code) =
834 codes.get(&format!("code/{}/{}", service, lang))
835 {
836 code.to_string()
837 } else {
838 return Ok::<_, std::convert::Infallible>(
839 hyper::Response::builder()
840 .status(404)
841 .body(http_body_util::Full::<hyper::body::Bytes>::new(
842 "404 Not Found".into(),
843 ))
844 .unwrap(),
845 );
846 };
847
848 return Ok::<_, std::convert::Infallible>(
849 hyper::Response::builder()
850 .status(200)
851 .header(
852 hyper::http::header::CONTENT_TYPE,
853 "text/javascript; charset=utf-8",
854 )
855 .body(http_body_util::Full::<hyper::body::Bytes>::new(code.into()))
856 .unwrap(),
857 );
858 }
859
860 #[cfg(feature = "doc")]
861 if path.starts_with("/doc") {
862 let parts: Vec<&str> = path
863 .trim_start_matches('/')
864 .trim_end_matches('/')
865 .split('/')
866 .collect();
867 let len = parts.len();
868 if len == 2 {
869 let code = if let Some(code) =
870 codes.get(&format!("doc/{}", parts[1]))
871 {
872 code.to_string()
873 } else {
874 return Ok::<_, std::convert::Infallible>(
875 hyper::Response::builder()
876 .status(404)
877 .body(http_body_util::Full::<hyper::body::Bytes>::new(
878 "404 Not Found".into(),
879 ))
880 .unwrap(),
881 );
882 };
883 return Ok::<_, std::convert::Infallible>(
884 hyper::Response::builder()
885 .status(200)
886 .header(
887 hyper::http::header::CONTENT_TYPE,
888 "application/json; charset=utf-8",
889 )
890 .body(http_body_util::Full::<hyper::body::Bytes>::new(code.into()))
891 .unwrap(),
892 );
893 } else if len == 1 {
894 let code = format!(
895 r#"{{"services":[{}]}}"#,
896 services
897 .iter()
898 .map(|(n, d, c)| format!(
899 r#"{{"name":"{}","desc":"{}","count":{}}}"#,
900 n, d, c
901 ))
902 .collect::<Vec<String>>()
903 .join(",")
904 );
905 return Ok::<_, std::convert::Infallible>(
906 hyper::Response::builder()
907 .status(200)
908 .header(
909 hyper::http::header::CONTENT_TYPE,
910 "application/json; charset=utf-8",
911 )
912 .body(http_body_util::Full::<hyper::body::Bytes>::new(code.into()))
913 .unwrap(),
914 );
915 } else {
916 return Ok::<_, std::convert::Infallible>(
917 hyper::Response::builder()
918 .status(404)
919 .body(http_body_util::Full::<hyper::body::Bytes>::new(
920 "404 Not Found".into(),
921 ))
922 .unwrap(),
923 );
924 }
925 } else {
926 static DIST_DIR: include_dir::Dir =
927 include_dir::include_dir!("$CARGO_MANIFEST_DIR/dist");
928
929 let path = req.uri().path().trim_start_matches('/');
930
931 let mut final_path = path;
932 if path.is_empty() || !DIST_DIR.get_file(path).is_some() {
933 final_path = "index.html";
934 }
935
936 let file = DIST_DIR.get_file(final_path);
937
938 if let Some(file) = file {
939 let mime = mime_guess::from_path(final_path)
940 .first_or_octet_stream();
941 return Ok::<_, std::convert::Infallible>(
942 hyper::Response::builder()
943 .status(200)
944 .header(
945 hyper::http::header::CONTENT_TYPE,
946 format!("{}; charset=utf-8", mime.as_ref()),
947 )
948 .body(http_body_util::Full::<hyper::body::Bytes>::new(
949 hyper::body::Bytes::from(file.contents()),
950 ))
951 .unwrap(),
952 );
953 } else {
954 return Ok::<_, std::convert::Infallible>(
955 hyper::Response::builder()
956 .status(404)
957 .body(http_body_util::Full::<hyper::body::Bytes>::new(
958 "404 Not Found".into(),
959 ))
960 .unwrap(),
961 );
962 }
963 }
964
965 #[cfg(all(feature = "code", not(feature = "doc")))]
966 return Ok::<_, std::convert::Infallible>(
967 hyper::Response::builder()
968 .status(404)
969 .body(http_body_util::Full::<hyper::body::Bytes>::new("404 Not Found".into()))
970 .unwrap(),
971 );
972 } else {
973 return Ok::<_, std::convert::Infallible>(
974 hyper::Response::builder()
975 .status(405)
976 .body(http_body_util::Full::<hyper::body::Bytes>::new(
977 "405 Method Not Allowed".into(),
978 ))
979 .unwrap(),
980 );
981 }
982
983 #[cfg(any(not(feature = "code"), not(feature = "doc")))]
984 return Ok::<_, std::convert::Infallible>(
985 hyper::Response::builder()
986 .status(404)
987 .body(http_body_util::Full::<hyper::body::Bytes>::new("404 Not Found".into()))
988 .unwrap(),
989 );
990 }
991 }),
992 )
993 .await
994 {
995 eprintln!("Error serving connection: {:?}", err);
996 }
997 });
998 }
999 }
1000 }
1001}
1002
1003pub trait AFastKind: Sized {
1004 fn kind() -> Kind;
1005
1006 fn field(name: &str) -> Field;
1007}
1008
1009#[derive(Debug)]
1010pub struct Tag {
1011 pub name: Option<String>,
1012 pub description: Option<String>,
1013 pub required: Option<String>,
1014 pub min: Option<(i64, String)>,
1015 pub max: Option<(i64, String)>,
1016}
1017
1018#[derive(Debug)]
1019pub struct Field {
1020 pub name: String,
1021 pub kind: Kind,
1022 pub tag: Option<Tag>,
1023}
1024
1025#[derive(Debug)]
1026pub enum Kind {
1027 Unit,
1028 I8,
1029 I16,
1030 I32,
1031 I64,
1032 I128,
1033 U8,
1034 U16,
1035 U32,
1036 U64,
1037 U128,
1038 F32,
1039 F64,
1040 Bool,
1041 String,
1042 Vec(Box<Kind>),
1043 Enum { variants: Vec<(Kind, String)> },
1044 Struct { fields: Vec<Field> },
1045 Tuple(Vec<Kind>),
1046 Nullable(Box<Kind>),
1047}