1
1
use anyhow:: Result ;
2
2
use axum:: {
3
- extract:: { Path , State } ,
4
- http:: StatusCode ,
5
- routing:: get,
6
- Router ,
3
+ extract:: { Path , State } , http:: StatusCode , response:: { Html , IntoResponse } , routing:: get, Router
7
4
} ;
8
5
use std:: { net:: SocketAddr , path:: PathBuf , sync:: Arc } ;
9
6
use tower_http:: services:: ServeDir ;
@@ -30,35 +27,82 @@ pub async fn process_http_serve(path: PathBuf, port: u16) -> Result<()> {
30
27
Ok ( ( ) )
31
28
}
32
29
30
+ fn is_dir ( path : & std:: path:: Path ) -> bool {
31
+ match std:: fs:: metadata ( path) {
32
+ Ok ( metadata) => metadata. is_dir ( ) ,
33
+ Err ( _) => false ,
34
+ }
35
+ }
36
+
37
+ fn remove_dir ( path : String , dir : String ) -> String {
38
+ if path. starts_with ( & dir) {
39
+ // 如果字符串以指定前缀开头,则通过切片去除前缀
40
+ path[ dir. len ( ) ..] . to_owned ( )
41
+ } else {
42
+ // 如果字符串不以指定前缀开头,则返回原始字符串
43
+ path
44
+ }
45
+ }
46
+
33
47
async fn file_handler (
34
48
State ( state) : State < Arc < HttpServeState > > ,
35
49
Path ( path) : Path < String > ,
36
- ) -> ( StatusCode , String ) {
50
+ ) -> ( StatusCode , impl IntoResponse ) {
37
51
let p = std:: path:: Path :: new ( & state. path ) . join ( path) ;
38
52
info ! ( "Reading file {:?}" , p) ;
39
53
if !p. exists ( ) {
40
54
(
41
55
StatusCode :: NOT_FOUND ,
42
- format ! ( "File {} note found" , p. display( ) ) ,
56
+ format ! ( "File {} note found" , p. display( ) ) . into_response ( ) ,
43
57
)
44
58
} else {
45
- // TODO: test p is a directory
46
- // if it is a directory, list all files/subdirectories
47
- // as <li><a href="/path/to/file">file name</a></li>
48
- // <html><body><ul>...</ul></body></html>
49
- match tokio:: fs:: read_to_string ( p) . await {
50
- Ok ( content) => {
51
- info ! ( "Read {} bytes" , content. len( ) ) ;
52
- ( StatusCode :: OK , content)
59
+ if is_dir ( & p) {
60
+ let mut entries = tokio:: fs:: read_dir ( p) . await . unwrap ( ) ;
61
+ let mut content = "" . to_string ( ) ;
62
+ while let Some ( entry) = entries. next_entry ( ) . await . unwrap ( ) {
63
+ let path = entry. path ( ) ;
64
+ content. push_str ( & format ! (
65
+ "<li><a href=\" \\ {}\" >{}</a></li>\n " ,
66
+ remove_dir( path. display( ) . to_string( ) , state. path. display( ) . to_string( ) ) ,
67
+ path. file_name( ) . unwrap( ) . to_str( ) . unwrap( ) . to_owned( ) ,
68
+ ) ) ;
53
69
}
54
- Err ( e) => {
55
- warn ! ( "Error reading file: {:?}" , e) ;
56
- ( StatusCode :: INTERNAL_SERVER_ERROR , e. to_string ( ) )
70
+
71
+ //content = format!("<html><body><ul>{}</ul></body></html>", content);
72
+ content = format ! (
73
+ r#"
74
+ <!DOCTYPE html>
75
+ <html>
76
+ <head>
77
+ <meta charset="utf-8">
78
+ <title>http server</title>
79
+ </head>
80
+ <body>
81
+ <ul>{}</ul>
82
+ </body>
83
+ </html>"# ,
84
+ content
85
+ ) ;
86
+ ( StatusCode :: OK , Html ( content) . into_response ( ) )
87
+ } else {
88
+ match tokio:: fs:: read_to_string ( p) . await {
89
+ Ok ( content) => {
90
+ info ! ( "Read {} bytes" , content. len( ) ) ;
91
+ ( StatusCode :: OK , content. into_response ( ) )
92
+ }
93
+ Err ( e) => {
94
+ warn ! ( "Error reading file: {:?}" , e) ;
95
+ (
96
+ StatusCode :: INTERNAL_SERVER_ERROR ,
97
+ e. to_string ( ) . into_response ( ) ,
98
+ )
99
+ }
57
100
}
58
101
}
59
102
}
60
103
}
61
104
105
+
62
106
#[ cfg( test) ]
63
107
mod tests {
64
108
use super :: * ;
0 commit comments