1+ use axum:: body:: Body ;
12use axum:: { routing:: get, serve, Router } ;
3+ use hyper:: header:: CONTENT_TYPE ;
4+ use hyper:: StatusCode ;
5+ use std:: sync:: Arc ;
6+
7+ use tower:: ServiceBuilder ;
8+
9+ use hyper:: header:: ACCEPT ;
10+ use hyper:: Request ;
211use std:: net:: SocketAddr ;
312use tokio:: signal:: unix:: { signal, SignalKind } ;
413use tokio:: { net:: TcpListener , sync:: mpsc} ;
514
15+ use axum:: middleware:: { from_fn, Next } ;
16+ use axum:: response:: { IntoResponse , Response } ;
17+
18+ #[ derive( Clone ) ]
19+ struct BrowserFriendlyJson {
20+ data : String ,
21+ }
22+
23+ struct JsonHtmlTemplate < ' a > {
24+ pre : & ' a str ,
25+ post : & ' a str ,
26+ }
27+
28+ impl IntoResponse for BrowserFriendlyJson {
29+ fn into_response ( self ) -> Response {
30+ let mut response = StatusCode :: NOT_IMPLEMENTED . into_response ( ) ;
31+ response. extensions_mut ( ) . insert ( self ) ;
32+ response
33+ }
34+ }
35+
36+ const SAMPLE_JSON : & str = include_str ! ( "sample.json" ) ;
37+
38+ const fn find_split_position ( bytes : & [ u8 ] ) -> usize {
39+ let mut i = 0 ;
40+ while i < bytes. len ( ) && ( bytes[ i] != b'{' || bytes[ i + 1 ] != b'}' ) {
41+ i += 1 ;
42+ }
43+ i
44+ // TODO(dkorolev): Panic if did not find `{}` or if found more than one `{}`.
45+ // NOTE(dkorolev): Why not create the split `str` slice at compile time, huh?
46+ }
47+
48+ static JSON_TEMPLATE_HTML : & [ u8 ] = include_bytes ! ( "jsontemplate.html" ) ;
49+ static JSON_TEMPLATE_HTML_SPLIT_IDX : usize = find_split_position ( & JSON_TEMPLATE_HTML ) ;
50+
51+ fn create_response < S : Into < String > > ( content_type : & str , body : S ) -> Response < Body > {
52+ Response :: builder ( ) . status ( StatusCode :: OK ) . header ( CONTENT_TYPE , content_type) . body ( Body :: from ( body. into ( ) ) ) . unwrap ( )
53+ }
54+
55+ async fn browser_json_renderer ( request : Request < Body > , next : Next , tmpl : Arc < JsonHtmlTemplate < ' _ > > ) -> Response {
56+ // TODO(dkorolev): Can this be more Rusty?
57+ let mut accept_html = false ;
58+ request. headers ( ) . get ( & ACCEPT ) . map ( |value| {
59+ let s = std:: str:: from_utf8 ( value. as_ref ( ) ) . unwrap ( ) ;
60+ s. split ( ',' ) . for_each ( |value| {
61+ if value == "text/html" || value == "html" {
62+ accept_html = true ;
63+ }
64+ } )
65+ } ) ;
66+
67+ // NOTE(dkorolev): I could not put the above logic to inside after `if let`, although, clearly it should be there.
68+ let mut response = next. run ( request) . await ;
69+ if let Some ( my_data) = response. extensions_mut ( ) . remove :: < BrowserFriendlyJson > ( ) {
70+ if accept_html {
71+ return create_response ( "text/html" , format ! ( "{}{}{}" , tmpl. pre, my_data. data, tmpl. post) ) ;
72+ } else {
73+ return create_response ( "application/json" , my_data. data ) ;
74+ }
75+ }
76+
77+ response
78+ }
79+
680#[ tokio:: main]
781async fn main ( ) {
82+ // NOTE(dkorolev): Can this be done at compile time?
83+ let html_template = Arc :: new ( JsonHtmlTemplate {
84+ pre : std:: str:: from_utf8 ( & JSON_TEMPLATE_HTML [ 0 ..JSON_TEMPLATE_HTML_SPLIT_IDX ] ) . expect ( "NON-UTF8 TEMPLATE" ) ,
85+ post : std:: str:: from_utf8 ( & JSON_TEMPLATE_HTML [ ( JSON_TEMPLATE_HTML_SPLIT_IDX + 2 ) ..] ) . expect ( "NON-UTF8 TEMPLATE" ) ,
86+ } ) ;
87+
888 let ( shutdown_tx, mut shutdown_rx) = mpsc:: channel :: < ( ) > ( 1 ) ;
989
1090 let app = Router :: new ( )
@@ -19,7 +99,13 @@ async fn main() {
1999 "yes i am shutting down\n "
20100 }
21101 } ) ,
22- ) ;
102+ )
103+ . route ( "/json" , get ( || async { BrowserFriendlyJson { data : SAMPLE_JSON . to_string ( ) } } ) )
104+ . layer ( ServiceBuilder :: new ( ) . layer ( from_fn ( {
105+ // TODO(dkorolev): Can I just move the `html_template` into `browser_json_renderer`?
106+ let html_template = Arc :: clone ( & html_template) ;
107+ move |req, next| browser_json_renderer ( req, next, Arc :: clone ( & html_template) )
108+ } ) ) ) ;
23109
24110 let addr = SocketAddr :: from ( ( [ 0 , 0 , 0 , 0 ] , 3000 ) ) ;
25111 let listener = TcpListener :: bind ( addr) . await . unwrap ( ) ;
0 commit comments