1
- use libherokubuildpack:: command:: CommandExt ;
2
- use libherokubuildpack:: write:: line_mapped;
1
+ use bullet_stream:: global:: print;
2
+ use bullet_stream:: style;
3
+ use fun_run:: CommandWithName ;
3
4
use std:: process:: { Command , Output } ;
4
5
use std:: time:: { Duration , Instant } ;
5
6
6
7
pub fn print_buildpack_name ( buildpack_name : impl AsRef < str > ) {
7
- let buildpack_name = buildpack_name. as_ref ( ) ;
8
- print ! ( "\n {ANSI_BUILDPACK_NAME_CODE}# {buildpack_name}{ANSI_RESET_CODE}\n \n " ) ;
8
+ print:: h2 ( buildpack_name) ;
9
9
}
10
10
11
11
pub fn print_section ( text : impl Into < BuildpackOutputText > ) {
12
- let text = text. into ( ) . to_ansi_string ( ) ;
13
- println ! ( "{ANSI_RESET_CODE}- {text}" ) ;
12
+ print:: bullet ( text. into ( ) . to_ansi_string ( ) ) ;
14
13
}
15
14
16
15
pub fn print_subsection ( text : impl Into < BuildpackOutputText > ) {
17
- let text = text. into ( ) . to_ansi_string ( ) ;
18
- println ! ( "{ANSI_RESET_CODE} - {text}" ) ;
16
+ print:: sub_bullet ( text. into ( ) . to_ansi_string ( ) ) ;
19
17
}
20
18
21
19
pub fn print_timing_done_subsection ( duration : & Duration ) {
@@ -24,42 +22,19 @@ pub fn print_timing_done_subsection(duration: &Duration) {
24
22
25
23
pub fn print_warning ( title : impl AsRef < str > , body : impl Into < BuildpackOutputText > ) {
26
24
let title = title. as_ref ( ) ;
27
-
28
- let mut sections = vec ! [ BuildpackOutputTextSection :: regular( format!(
29
- "WARNING: {title}\n \n "
30
- ) ) ] ;
31
-
32
- let mut body = body. into ( ) ;
33
- sections. append ( & mut body. sections ) ;
34
-
35
- let text = BuildpackOutputText {
36
- default_code : Some ( String :: from ( ANSI_YELLOW_CODE ) ) ,
37
- line_prefix : Some ( String :: from ( "! " ) ) ,
38
- sections,
39
- ..BuildpackOutputText :: default ( )
40
- } ;
41
-
42
- eprintln ! ( "{}" , text. to_ansi_string( ) ) ;
25
+ print:: warning ( format ! (
26
+ "WARNING: {title}\n \n {}" ,
27
+ body. into( ) . to_ansi_string( )
28
+ ) ) ;
43
29
}
44
30
45
31
pub fn print_error ( title : impl AsRef < str > , body : impl Into < BuildpackOutputText > ) {
46
32
let title = title. as_ref ( ) ;
47
33
48
- let mut sections = vec ! [ BuildpackOutputTextSection :: regular( format!(
49
- "ERROR: {title}\n \n "
50
- ) ) ] ;
51
-
52
- let mut body = body. into ( ) ;
53
- sections. append ( & mut body. sections ) ;
54
-
55
- let text = BuildpackOutputText {
56
- default_code : Some ( String :: from ( ANSI_RED_CODE ) ) ,
57
- line_prefix : Some ( String :: from ( ERROR_WARNING_LINE_PREFIX ) ) ,
58
- sections,
59
- ..BuildpackOutputText :: default ( )
60
- } ;
61
-
62
- eprintln ! ( "{}" , text. to_ansi_string( ) ) ;
34
+ print:: error ( format ! (
35
+ "ERROR: {title}\n \n {}" ,
36
+ body. into( ) . to_ansi_string( )
37
+ ) ) ;
63
38
}
64
39
65
40
pub fn run_command < E , F : FnOnce ( std:: io:: Error ) -> E , F2 : FnOnce ( Output ) -> E > (
@@ -68,47 +43,25 @@ pub fn run_command<E, F: FnOnce(std::io::Error) -> E, F2: FnOnce(Output) -> E>(
68
43
io_error_fn : F ,
69
44
exit_status_fn : F2 ,
70
45
) -> Result < Output , E > {
71
- let child = if quiet {
72
- command. output_and_write_streams ( std:: io:: sink ( ) , std:: io:: sink ( ) )
46
+ let title = format ! ( "Running {}" , style:: value( command. name( ) ) ) ;
47
+ if quiet {
48
+ let _timer = print:: sub_start_timer ( title) ;
49
+ command. named_output ( )
73
50
} else {
74
- const SPACE_ASCII : u8 = 0x20 ;
75
- let prefix = vec ! [ SPACE_ASCII ; 6 ] ;
76
-
77
- println ! ( ) ;
78
-
79
- let output = command. output_and_write_streams (
80
- line_mapped ( std:: io:: stdout ( ) , add_prefix_to_non_empty ( prefix. clone ( ) ) ) ,
81
- line_mapped ( std:: io:: stderr ( ) , add_prefix_to_non_empty ( prefix) ) ,
82
- ) ;
83
-
84
- println ! ( ) ;
85
-
86
- output
87
- } ;
88
-
89
- child. map_err ( io_error_fn) . and_then ( |output| {
90
- if output. status . success ( ) {
91
- Ok ( output)
92
- } else {
93
- Err ( exit_status_fn ( output) )
51
+ print:: sub_stream_with ( & title, |stdout, stderr| {
52
+ command. stream_output ( stdout, stderr)
53
+ } )
54
+ }
55
+ . map ( Into :: < Output > :: into)
56
+ . map_err ( |o| match o {
57
+ fun_run:: CmdError :: SystemError ( _, error) => io_error_fn ( error) ,
58
+ fun_run:: CmdError :: NonZeroExitNotStreamed ( named_output)
59
+ | fun_run:: CmdError :: NonZeroExitAlreadyStreamed ( named_output) => {
60
+ exit_status_fn ( Into :: < Output > :: into ( named_output) )
94
61
}
95
62
} )
96
63
}
97
64
98
- fn add_prefix_to_non_empty < P : Into < Vec < u8 > > > ( prefix : P ) -> impl Fn ( Vec < u8 > ) -> Vec < u8 > {
99
- let prefix = prefix. into ( ) ;
100
-
101
- move |mut input| {
102
- if input. is_empty ( ) {
103
- vec ! [ ]
104
- } else {
105
- let mut result = prefix. clone ( ) ;
106
- result. append ( & mut input) ;
107
- result
108
- }
109
- }
110
- }
111
-
112
65
#[ derive( Clone , Debug ) ]
113
66
pub struct BuildpackOutputText {
114
67
pub line_prefix : Option < String > ,
@@ -297,94 +250,5 @@ fn format_duration(duration: &Duration) -> String {
297
250
const VALUE_DELIMITER_CHAR : char = '`' ;
298
251
const ANSI_RESET_CODE : & str = "\u{1b} [0m" ;
299
252
const ANSI_VALUE_CODE : & str = "\u{1b} [0;33m" ;
300
- const ANSI_YELLOW_CODE : & str = "\u{1b} [0;33m" ;
301
- const ANSI_RED_CODE : & str = "\u{1b} [0;31m" ;
302
- const ANSI_BUILDPACK_NAME_CODE : & str = "\u{1b} [1;35m" ;
303
253
const ANSI_URL_CODE : & str = "\u{1b} [0;34m" ;
304
254
const ANSI_COMMAND_CODE : & str = "\u{1b} [1;36m" ;
305
- const ERROR_WARNING_LINE_PREFIX : & str = "! " ;
306
-
307
- #[ cfg( test) ]
308
- mod test {
309
- use super :: * ;
310
-
311
- #[ test]
312
- fn test_prefixing ( ) {
313
- const DEFAULT_CODE : & str = "\x1B [0;33m" ;
314
-
315
- let text = BuildpackOutputText {
316
- default_code : Some ( String :: from ( DEFAULT_CODE ) ) ,
317
- sections : vec ! [
318
- BuildpackOutputTextSection :: regular( "Hello\n " ) ,
319
- BuildpackOutputTextSection :: value( "World" ) ,
320
- BuildpackOutputTextSection :: regular( "\n " ) ,
321
- BuildpackOutputTextSection :: regular( "How\n are you?" ) ,
322
- ] ,
323
- line_prefix : Some ( String :: from ( ERROR_WARNING_LINE_PREFIX ) ) ,
324
- ..Default :: default ( )
325
- } ;
326
-
327
- assert_eq ! ( text. to_ansi_string( ) , "\u{1b} [0m\u{1b} [0;33m! Hello\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! `\u{1b} [0;33mWorld\u{1b} [0m`\u{1b} [0;33m\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! How\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! are you?" ) ;
328
- }
329
-
330
- #[ test]
331
- fn test_prefixing_with_value ( ) {
332
- let text = BuildpackOutputText {
333
- default_code : Some ( String :: from ( ANSI_YELLOW_CODE ) ) ,
334
- sections : vec ! [
335
- BuildpackOutputTextSection :: regular( "Intro\n " ) ,
336
- BuildpackOutputTextSection :: value( "With\n Newline" ) ,
337
- BuildpackOutputTextSection :: regular( "\n Outro" ) ,
338
- ] ,
339
- line_prefix : Some ( String :: from ( "! " ) ) ,
340
- ..Default :: default ( )
341
- } ;
342
-
343
- assert_eq ! (
344
- text. to_ansi_string( ) ,
345
- "\u{1b} [0m\u{1b} [0;33m! Intro\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! `\u{1b} [0;33mWith\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! \u{1b} [0;33mNewline\u{1b} [0m`\u{1b} [0;33m\u{1b} [0m\n \u{1b} [0m\u{1b} [0;33m! Outro"
346
- ) ;
347
- }
348
-
349
- #[ test]
350
- fn test_display_duration ( ) {
351
- let duration = Duration :: ZERO ;
352
- assert_eq ! ( format_duration( & duration) , "< 0.1s" ) ;
353
-
354
- let duration = Duration :: from_millis ( 99 ) ;
355
- assert_eq ! ( format_duration( & duration) , "< 0.1s" ) ;
356
-
357
- let duration = Duration :: from_millis ( 100 ) ;
358
- assert_eq ! ( format_duration( & duration) , "0.1s" ) ;
359
-
360
- let duration = Duration :: from_millis ( 210 ) ;
361
- assert_eq ! ( format_duration( & duration) , "0.2s" ) ;
362
-
363
- let duration = Duration :: from_millis ( 1100 ) ;
364
- assert_eq ! ( format_duration( & duration) , "1.1s" ) ;
365
-
366
- let duration = Duration :: from_millis ( 9100 ) ;
367
- assert_eq ! ( format_duration( & duration) , "9.1s" ) ;
368
-
369
- let duration = Duration :: from_millis ( 10100 ) ;
370
- assert_eq ! ( format_duration( & duration) , "10.1s" ) ;
371
-
372
- let duration = Duration :: from_millis ( 52100 ) ;
373
- assert_eq ! ( format_duration( & duration) , "52.1s" ) ;
374
-
375
- let duration = Duration :: from_millis ( 60 * 1000 ) ;
376
- assert_eq ! ( format_duration( & duration) , "1m 0s" ) ;
377
-
378
- let duration = Duration :: from_millis ( 60 * 1000 + 2000 ) ;
379
- assert_eq ! ( format_duration( & duration) , "1m 2s" ) ;
380
-
381
- let duration = Duration :: from_millis ( 60 * 60 * 1000 - 1 ) ;
382
- assert_eq ! ( format_duration( & duration) , "59m 59s" ) ;
383
-
384
- let duration = Duration :: from_millis ( 60 * 60 * 1000 ) ;
385
- assert_eq ! ( format_duration( & duration) , "1h 0m 0s" ) ;
386
-
387
- let duration = Duration :: from_millis ( 75 * 60 * 1000 - 1 ) ;
388
- assert_eq ! ( format_duration( & duration) , "1h 14m 59s" ) ;
389
- }
390
- }
0 commit comments