1+ use anyhow:: { Result , anyhow} ;
12use tabular:: { Row , Table } ;
23use yansi:: Paint ;
34
@@ -7,41 +8,78 @@ pub mod boat_data;
78pub mod log;
89pub mod tag;
910
11+ /// A single displayable column: (key used in --fields/config, header label, alignment)
12+ pub type FieldSpec = ( & ' static str , & ' static str , char ) ;
13+
1014pub trait TablePrintable {
11- fn to_printable_table ( & self ) -> Table ;
15+ /// Renders as a table. `fields` selects and orders columns by key (see `RowPrintable::field_specs`).
16+ /// `None` uses every column in its default order.
17+ fn to_printable_table ( & self , fields : Option < & [ String ] > ) -> Result < Table > ;
1218}
1319
1420pub trait RowPrintable {
15- fn row_spec ( ) -> String ;
16- fn header_names ( ) -> Vec < String > ;
21+ /// Every available column, in default display order.
22+ fn field_specs ( ) -> Vec < FieldSpec > ;
23+ /// Values in the same order as `field_specs()`.
1724 fn row_values ( & self ) -> Vec < String > ;
1825 fn style_cell ( & self , value : String ) -> String {
1926 value
2027 }
2128}
2229
30+ fn resolve_field_indices ( specs : & [ FieldSpec ] , fields : Option < & [ String ] > ) -> Result < Vec < usize > > {
31+ let Some ( keys) = fields else {
32+ return Ok ( ( 0 ..specs. len ( ) ) . collect ( ) ) ;
33+ } ;
34+
35+ keys. iter ( )
36+ . map ( |key| {
37+ specs
38+ . iter ( )
39+ . position ( |( field_key, _, _) | field_key. eq_ignore_ascii_case ( key) )
40+ . ok_or_else ( || {
41+ let available = specs
42+ . iter ( )
43+ . map ( |( k, _, _) | * k)
44+ . collect :: < Vec < _ > > ( )
45+ . join ( ", " ) ;
46+ anyhow ! ( "unknown field '{key}' (available: {available})" )
47+ } )
48+ } )
49+ . collect ( )
50+ }
51+
2352impl < T > TablePrintable for Vec < T >
2453where
2554 T : RowPrintable ,
2655{
27- fn to_printable_table ( & self ) -> Table {
28- let mut table = Table :: new ( & T :: row_spec ( ) ) ;
56+ fn to_printable_table ( & self , fields : Option < & [ String ] > ) -> Result < Table > {
57+ let specs = T :: field_specs ( ) ;
58+ let indices = resolve_field_indices ( & specs, fields) ?;
59+
60+ let row_spec = indices
61+ . iter ( )
62+ . map ( |& i| format ! ( "{{:{}}}" , specs[ i] . 2 ) )
63+ . collect :: < Vec < _ > > ( )
64+ . join ( " " ) ;
65+ let mut table = Table :: new ( & row_spec) ;
2966
3067 let mut header = Row :: new ( ) ;
31- for h in T :: header_names ( ) {
32- header. add_ansi_cell ( Paint :: new ( h ) . underline ( ) . to_string ( ) ) ;
68+ for & i in & indices {
69+ header. add_ansi_cell ( Paint :: new ( specs [ i ] . 1 ) . underline ( ) . to_string ( ) ) ;
3370 }
3471 table. add_row ( header) ;
3572
3673 for item in self . iter ( ) {
74+ let values = item. row_values ( ) ;
3775 let mut row = Row :: new ( ) ;
38- for value in item . row_values ( ) {
39- let styled = item. style_cell ( value . to_string ( ) ) ;
76+ for & i in & indices {
77+ let styled = item. style_cell ( values [ i ] . clone ( ) ) ;
4078 row. add_ansi_cell ( styled) ;
4179 }
4280 table. add_row ( row) ;
4381 }
4482
45- table
83+ Ok ( table)
4684 }
4785}
0 commit comments