@@ -5,6 +5,7 @@ use std::{
55 io:: { Write , stdout} ,
66 net:: { TcpStream , ToSocketAddrs } ,
77 process,
8+ time:: Instant ,
89} ;
910
1011use http:: { HttpMethod , HttpRequest , HttpResponse } ;
@@ -13,11 +14,89 @@ use config::ClientConfig;
1314
1415use crate :: config:: HttpType ;
1516
16- fn open_file ( fname : & str ) -> Box < dyn Write > {
17- Box :: new ( File :: create ( fname) . unwrap_or_else ( |_| {
17+ pub struct ProgressWriter < W : Write > {
18+ max : usize ,
19+ current : usize ,
20+ last_update : Instant ,
21+ prev_current : usize ,
22+ writer : W ,
23+ }
24+
25+ impl < W : Write > ProgressWriter < W > {
26+ fn start ( & self ) {
27+ println ! (
28+ "{perc:3} {progress:8} {total:^8} speed" ,
29+ perc = "%" ,
30+ progress = "Progress" ,
31+ total = "Total" ,
32+ ) ;
33+ }
34+
35+ fn print_progress ( & self ) {
36+ let Self { max, current, .. } = self ;
37+
38+ fn get_unit ( bytes : usize ) -> String {
39+ let kb = bytes / 1000 ;
40+ if kb >= 1000 {
41+ format ! ( "{} M" , kb / 1000 )
42+ } else {
43+ format ! ( "{kb} K" )
44+ }
45+ }
46+
47+ let curr = get_unit ( * current) ;
48+ let max = get_unit ( * max) ;
49+
50+ if self . max > 0 {
51+ let percentage = 100 * self . current / self . max ;
52+ print ! ( "\x1b [2K\r {percentage:<3} {curr:^8} {max:^8} " ) ;
53+ } else {
54+ print ! ( "\x1b [2K\r {current}" ) ;
55+ }
56+ let kbps = ( self . current - self . prev_current ) / 1000 ;
57+ if kbps >= 1000 {
58+ print ! ( "{} Mb/s" , kbps / 1000 ) ;
59+ } else {
60+ print ! ( "{kbps} Kb/s" ) ;
61+ }
62+ stdout ( ) . flush ( ) . unwrap ( ) ;
63+ }
64+ }
65+
66+ impl < W : Write > Write for ProgressWriter < W > {
67+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
68+ let n = self . writer . write ( buf) ?;
69+ self . current += n;
70+
71+ let time = Instant :: now ( ) . duration_since ( self . last_update ) ;
72+ if time. as_millis ( ) >= 1000 {
73+ self . print_progress ( ) ;
74+ self . last_update = Instant :: now ( ) ;
75+ self . prev_current = self . current ;
76+ }
77+
78+ Ok ( n)
79+ }
80+
81+ fn flush ( & mut self ) -> io:: Result < ( ) > {
82+ self . writer . flush ( )
83+ }
84+ }
85+
86+ fn open_file ( fname : & str , max : usize ) -> Box < dyn Write > {
87+ let file = File :: create ( fname) . unwrap_or_else ( |_| {
1888 eprintln ! ( "Couldn't create file: {fname}" ) ;
1989 process:: exit ( 1 ) ;
20- } ) )
90+ } ) ;
91+ let p = Box :: new ( ProgressWriter {
92+ max,
93+ current : 0 ,
94+ prev_current : 0 ,
95+ writer : file,
96+ last_update : Instant :: now ( ) ,
97+ } ) ;
98+ p. start ( ) ;
99+ p
21100}
22101
23102#[ cfg( not( feature = "tls" ) ) ]
@@ -68,20 +147,6 @@ pub fn main() -> http::Result<()> {
68147 let addr = format ! ( "{}:{}" , conf. host, conf. port) ;
69148 let addrs = addr. to_socket_addrs ( ) . unwrap ( ) . next ( ) . unwrap ( ) ;
70149
71- let mut out: Box < dyn Write > = match conf. out_file {
72- config:: OutFile :: Stdout => Box :: new ( stdout ( ) ) ,
73- config:: OutFile :: Filename ( s) => open_file ( & s) ,
74- config:: OutFile :: GetFromUrl => {
75- let fname = conf
76- . url
77- . split ( '/' )
78- . filter ( |s| !s. is_empty ( ) )
79- . next_back ( )
80- . unwrap_or ( & conf. host ) ;
81- open_file ( fname)
82- }
83- } ;
84-
85150 let req = HttpRequest :: builder ( )
86151 . method ( conf. method )
87152 . url ( conf. url . clone ( ) . into_boxed_str ( ) )
@@ -107,6 +172,21 @@ pub fn main() -> http::Result<()> {
107172 process:: exit ( 1 ) ;
108173 } ) ;
109174
175+ let len = result. content_length ( ) ;
176+ let mut out: Box < dyn Write > = match conf. out_file {
177+ config:: OutFile :: Stdout => Box :: new ( stdout ( ) ) ,
178+ config:: OutFile :: Filename ( s) => open_file ( & s, len) ,
179+ config:: OutFile :: GetFromUrl => {
180+ let fname = conf
181+ . url
182+ . split ( '/' )
183+ . filter ( |s| !s. is_empty ( ) )
184+ . next_back ( )
185+ . unwrap_or ( & conf. host ) ;
186+ open_file ( fname, len)
187+ }
188+ } ;
189+
110190 if matches ! ( conf. method, HttpMethod :: HEAD ) {
111191 println ! ( "Headers" ) ;
112192 for ( k, v) in result. headers ( ) {
0 commit comments