22//!
33//! All usage errors will pass through the Error enum, a lot of them in the Error::Custom.
44
5+ use crate :: formats:: { self , SupportedOp } ;
6+ use crate :: utils:: colors:: { BLUE , GREEN , RESET , YELLOW } ;
7+ use crate :: { accessible:: is_running_in_accessible_mode, utils:: os_str_to_str} ;
58use std:: {
69 borrow:: Cow ,
710 ffi:: OsString ,
811 fmt:: { self , Display } ,
912 io,
1013} ;
1114
12- use crate :: {
13- accessible:: is_running_in_accessible_mode,
14- extension:: { PRETTY_SUPPORTED_ALIASES , PRETTY_SUPPORTED_EXTENSIONS } ,
15- utils:: os_str_to_str,
16- } ;
17-
1815/// All errors that can be generated by `ouch`
1916#[ derive( Debug , Clone ) ]
2017pub enum Error {
@@ -43,7 +40,7 @@ pub enum Error {
4340 /// From sevenz_rust::Error
4441 SevenzipError { reason : String } ,
4542 /// Recognised but unsupported format
46- // currently only RAR when built without the `unrar` feature
43+ /// ( currently only RAR when built without the `unrar` feature)
4744 UnsupportedFormat { reason : String } ,
4845 /// Invalid password provided
4946 InvalidPassword { reason : String } ,
@@ -58,11 +55,11 @@ pub type CowStr = Cow<'static, str>;
5855/// Pretty final error message for end users, crashing the program after display.
5956#[ derive( Clone , Debug , Default , PartialEq , Eq ) ]
6057pub struct FinalError {
61- /// Should be made of just one line, appears after the "\ [ERROR\]" part
58+ /// Single line shown after ` [ERROR]`
6259 title : CowStr ,
63- /// Shown as a unnumbered list in yellow
60+ /// Bullet list in yellow
6461 details : Vec < CowStr > ,
65- /// Shown as green at the end to give hints on how to work around this error, if it's fixable
62+ /// Hints in green at the end
6663 hints : Vec < CowStr > ,
6764}
6865
@@ -71,8 +68,6 @@ impl Display for FinalError {
7168 use crate :: utils:: colors:: * ;
7269
7370 // Title
74- //
75- // When in ACCESSIBLE mode, the square brackets are suppressed
7671 if is_running_in_accessible_mode ( ) {
7772 write ! ( f, "{}ERROR{}: {}" , * RED , * RESET , self . title) ?;
7873 } else {
@@ -88,9 +83,8 @@ impl Display for FinalError {
8883 if !self . hints . is_empty ( ) {
8984 // Separate by one blank line.
9085 writeln ! ( f) ?;
91- // to reduce redundant output for text-to-speech systems, braille
92- // displays and so on, only print "hints" once in ACCESSIBLE mode
9386 if is_running_in_accessible_mode ( ) {
87+ // In ACCESSIBLE mode, print the "hints" header once.
9488 write ! ( f, "\n {}hints:{}" , * GREEN , * RESET ) ?;
9589 for hint in & self . hints {
9690 write ! ( f, "\n {hint}" ) ?;
@@ -107,7 +101,7 @@ impl Display for FinalError {
107101}
108102
109103impl FinalError {
110- /// Only constructor
104+ /// Constructor with title only.
111105 #[ must_use]
112106 pub fn with_title ( title : impl Into < CowStr > ) -> Self {
113107 Self {
@@ -117,30 +111,88 @@ impl FinalError {
117111 }
118112 }
119113
120- /// Add one detail line, can have multiple
114+ /// Adds one detail line ( can be called multiple times).
121115 #[ must_use]
122116 pub fn detail ( mut self , detail : impl Into < CowStr > ) -> Self {
123117 self . details . push ( detail. into ( ) ) ;
124118 self
125119 }
126120
127- /// Add one hint line, can have multiple
121+ /// Adds one hint line ( can be called multiple times).
128122 #[ must_use]
129123 pub fn hint ( mut self , hint : impl Into < CowStr > ) -> Self {
130124 self . hints . push ( hint. into ( ) ) ;
131125 self
132126 }
133127
134- /// Adds all supported formats as hints.
128+ // ---------------------------------------------------------------------
129+ // Compact, colored lists of supported formats (DRY helper + 3 public APIs)
130+ // ---------------------------------------------------------------------
131+
132+ /// Internal helper to add compact, colored supported-lists.
135133 ///
136- /// This is what it looks like:
137- /// ```
138- /// hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, lz, sz, zst
139- /// hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz
140- /// ```
134+ /// - If `show_compress` is true, prints the compress-only list.
135+ /// - If `show_decompress` is true, prints the decompress-only list.
136+ /// - Always appends a colored hint for `ouch list-formats`.
137+ fn hint_supported_lists ( mut self , show_compress : bool , show_decompress : bool ) -> Self {
138+ // Color each token in a comma-separated list.
139+ let color_csv = |csv : String | -> String {
140+ csv. split ( ", " )
141+ . map ( |t| format ! ( "{}{}{}" , * GREEN , t, * RESET ) )
142+ . collect :: < Vec < _ > > ( )
143+ . join ( ", " )
144+ } ;
145+
146+ if show_compress {
147+ let compress = formats:: compact_extensions ( SupportedOp :: Compress ) ;
148+ let compress_colored = color_csv ( compress) ;
149+ self = self . hint ( format ! (
150+ "Supported extensions for {}compression{} are: {}" ,
151+ * YELLOW , * RESET , compress_colored
152+ ) ) ;
153+ }
154+
155+ if show_decompress {
156+ let decompress = formats:: compact_extensions ( SupportedOp :: Decompress ) ;
157+ let decompress_colored = color_csv ( decompress) ;
158+ self = self . hint ( format ! (
159+ "Supported extensions for {}decompression{} are: {}" ,
160+ * YELLOW , * RESET , decompress_colored
161+ ) ) ;
162+ }
163+
164+ let aliases = formats:: compact_shorthands ( ) ;
165+ if !aliases. is_empty ( ) {
166+ let aliases_colored = aliases
167+ . split ( ", " )
168+ . map ( |t| format ! ( "{}{}{}" , * GREEN , t, * RESET ) )
169+ . collect :: < Vec < _ > > ( )
170+ . join ( ", " ) ;
171+ self = self . hint ( format ! (
172+ "Supported {}aliases{} are: {}" ,
173+ * YELLOW , * RESET , aliases_colored
174+ ) ) ;
175+ }
176+
177+ self . hint ( format ! (
178+ "For detailed capabilities and notes, run: {}ouch list-formats{}" ,
179+ * BLUE , * RESET
180+ ) )
181+ }
182+
183+ /// Adds a single, multi-line hint with the compact lists (compress + decompress).
141184 pub fn hint_all_supported_formats ( self ) -> Self {
142- self . hint ( format ! ( "Supported extensions are: {PRETTY_SUPPORTED_EXTENSIONS}" ) )
143- . hint ( format ! ( "Supported aliases are: {PRETTY_SUPPORTED_ALIASES}" ) )
185+ self . hint_supported_lists ( true , true )
186+ }
187+
188+ /// Adds a compact list filtered to compression only.
189+ pub fn hint_supported_formats_for_compress ( self ) -> Self {
190+ self . hint_supported_lists ( true , false )
191+ }
192+
193+ /// Adds a compact list filtered to decompression only.
194+ pub fn hint_supported_formats_for_decompress ( self ) -> Self {
195+ self . hint_supported_lists ( false , true )
144196 }
145197}
146198
0 commit comments