@@ -6,21 +6,165 @@ fn main() {
66
77#[ cfg( feature = "cli" ) ]
88fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
9+ use core:: fmt;
910 use css_inline:: { CSSInliner , DefaultStylesheetResolver , InlineOptions } ;
1011 use rayon:: prelude:: * ;
1112 use std:: {
1213 borrow:: Cow ,
14+ env,
15+ error:: Error ,
1316 ffi:: OsString ,
14- fmt:: { Display , Write as FmtWrite } ,
17+ fmt:: Write as FmtWrite ,
1518 fs:: { read_to_string, File } ,
1619 io:: { self , Read , Write } ,
1720 path:: Path ,
21+ str:: FromStr ,
1822 sync:: {
1923 atomic:: { AtomicI32 , Ordering } ,
2024 Arc ,
2125 } ,
2226 } ;
2327
28+ fn parse_url ( url : Option < & str > ) -> Result < Option < url:: Url > , url:: ParseError > {
29+ Ok ( if let Some ( url) = url {
30+ Some ( url:: Url :: parse ( url) ?)
31+ } else {
32+ None
33+ } )
34+ }
35+
36+ #[ derive( Debug ) ]
37+ struct ParseError {
38+ message : String ,
39+ }
40+
41+ impl fmt:: Display for ParseError {
42+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
43+ write ! ( f, "{}" , self . message)
44+ }
45+ }
46+
47+ impl Error for ParseError { }
48+
49+ struct ParsedArgs {
50+ help : bool ,
51+ version : bool ,
52+ files : Vec < String > ,
53+ inline_style_tags : bool ,
54+ keep_style_tags : bool ,
55+ keep_link_tags : bool ,
56+ base_url : Option < String > ,
57+ extra_css : Option < String > ,
58+ output_filename_prefix : Option < OsString > ,
59+ load_remote_stylesheets : bool ,
60+ #[ cfg( feature = "stylesheet-cache" ) ]
61+ cache_size : Option < usize > ,
62+ }
63+
64+ impl Default for ParsedArgs {
65+ fn default ( ) -> Self {
66+ Self {
67+ help : false ,
68+ version : false ,
69+ files : Vec :: new ( ) ,
70+ inline_style_tags : true ,
71+ keep_style_tags : false ,
72+ keep_link_tags : false ,
73+ base_url : None ,
74+ extra_css : None ,
75+ output_filename_prefix : None ,
76+ load_remote_stylesheets : false ,
77+ #[ cfg( feature = "stylesheet-cache" ) ]
78+ cache_size : None ,
79+ }
80+ }
81+ }
82+
83+ #[ cfg( feature = "stylesheet-cache" ) ]
84+ macro_rules! if_cfg_feature_stylesheet_cache {
85+ ( $val: expr) => {
86+ $val
87+ } ;
88+ }
89+
90+ #[ cfg( not( feature = "stylesheet-cache" ) ) ]
91+ macro_rules! if_cfg_feature_stylesheet_cache {
92+ // Empty string that won't match
93+ ( $val: expr) => {
94+ ""
95+ } ;
96+ }
97+
98+ fn requires_value ( flag : & str ) -> bool {
99+ matches ! (
100+ flag,
101+ "inline-style-tags"
102+ | "base-url"
103+ | "extra-css"
104+ | "output-filename-prefix"
105+ | if_cfg_feature_stylesheet_cache!( "cache-size" )
106+ )
107+ }
108+
109+ fn parse_value < T > ( value : & str , flag : & str ) -> Result < T , ParseError >
110+ where
111+ T : FromStr ,
112+ T :: Err : fmt:: Display ,
113+ {
114+ value. parse :: < T > ( ) . map_err ( |e| ParseError {
115+ message : format ! ( "Failed to parse value '{value}' for flag '{flag}': {e}" ) ,
116+ } )
117+ }
118+
119+ fn handle_flag_with_value (
120+ parsed : & mut ParsedArgs ,
121+ flag : & str ,
122+ value : & str ,
123+ ) -> Result < ( ) , ParseError > {
124+ match flag {
125+ "inline-style-tags" => parsed. inline_style_tags = parse_value ( value, flag) ?,
126+ "base-url" => parsed. base_url = Some ( value. to_string ( ) ) ,
127+ "extra-css" => parsed. extra_css = Some ( value. to_string ( ) ) ,
128+ "output-filename-prefix" => {
129+ parsed. output_filename_prefix = Some ( value. to_string ( ) . into ( ) ) ;
130+ }
131+ #[ cfg( feature = "stylesheet-cache" ) ]
132+ "cache-size" => parsed. cache_size = Some ( parse_value ( value, flag) ?) ,
133+ _ => {
134+ return Err ( ParseError {
135+ message : format ! ( "Unknown flag: --{flag}" ) ,
136+ } )
137+ }
138+ }
139+ Ok ( ( ) )
140+ }
141+
142+ fn handle_boolean_flag ( parsed : & mut ParsedArgs , flag : & str ) -> Result < ( ) , ParseError > {
143+ match flag {
144+ "help" | "h" => parsed. help = true ,
145+ "version" | "v" => parsed. version = true ,
146+ "keep-style-tags" => parsed. keep_style_tags = true ,
147+ "keep-link-tags" => parsed. keep_link_tags = true ,
148+ "load-remote-stylesheets" => parsed. load_remote_stylesheets = true ,
149+ _ => {
150+ return Err ( ParseError {
151+ message : format ! ( "Unknown flag: {flag}" ) ,
152+ } )
153+ }
154+ }
155+ Ok ( ( ) )
156+ }
157+
158+ fn format_error ( filename : Option < & str > , error : impl fmt:: Display ) {
159+ let mut buffer = String :: with_capacity ( 128 ) ;
160+ if let Some ( filename) = filename {
161+ writeln ! ( buffer, "Filename: {filename}" ) . expect ( "Failed to write to buffer" ) ;
162+ }
163+ buffer. push_str ( "Status: ERROR\n " ) ;
164+ writeln ! ( buffer, "Details: {error}" ) . expect ( "Failed to write to buffer" ) ;
165+ eprintln ! ( "{}" , buffer. trim( ) ) ;
166+ }
167+
24168 const VERSION_MESSAGE : & [ u8 ] =
25169 concat ! ( "css-inline " , env!( "CARGO_PKG_VERSION" ) , "\n " ) . as_bytes ( ) ;
26170 const HELP_MESSAGE : & [ u8 ] = concat ! (
@@ -74,86 +218,76 @@ OPTIONS:
74218 )
75219 . as_bytes ( ) ;
76220
77- struct Args {
78- inline_style_tags : bool ,
79- keep_style_tags : bool ,
80- keep_link_tags : bool ,
81- base_url : Option < String > ,
82- extra_css : Option < String > ,
83- output_filename_prefix : Option < OsString > ,
84- load_remote_stylesheets : bool ,
85- #[ cfg( feature = "stylesheet-cache" ) ]
86- cache_size : Option < usize > ,
87- files : Vec < String > ,
88- }
221+ let mut raw_args = env:: args ( ) . skip ( 1 ) ;
222+ let mut args = ParsedArgs :: default ( ) ;
89223
90- fn parse_url ( url : Option < String > ) -> Result < Option < url:: Url > , url:: ParseError > {
91- Ok ( if let Some ( url) = url {
92- Some ( url:: Url :: parse ( url. as_str ( ) ) ?)
224+ while let Some ( arg) = raw_args. next ( ) {
225+ if let Some ( flag) = arg. strip_prefix ( "--" ) {
226+ // Handle --key=value format
227+ if let Some ( ( flag, value) ) = flag. split_once ( '=' ) {
228+ handle_flag_with_value ( & mut args, flag, value) ?;
229+ } else {
230+ // Handle --key format (boolean or expecting value)
231+ if requires_value ( flag) {
232+ // Expects a value
233+ if let Some ( value) = raw_args. next ( ) {
234+ handle_flag_with_value ( & mut args, flag, & value) ?;
235+ } else {
236+ eprintln ! ( "Error parsing arguments: Flag --{flag} requires a value" ) ;
237+ std:: process:: exit ( 1 ) ;
238+ }
239+ } else {
240+ // Boolean flag
241+ handle_boolean_flag ( & mut args, flag) ?;
242+ }
243+ }
244+ } else if let Some ( flag) = arg. strip_prefix ( '-' ) {
245+ if flag. len ( ) == 1 {
246+ // Single character short flag
247+ handle_boolean_flag ( & mut args, flag) ?;
248+ } else {
249+ eprintln ! ( "Error parsing arguments: Invalid flag: -{flag}" ) ;
250+ std:: process:: exit ( 1 ) ;
251+ }
93252 } else {
94- None
95- } )
96- }
97-
98- fn format_error ( filename : Option < & str > , error : impl Display ) {
99- let mut buffer = String :: with_capacity ( 128 ) ;
100- if let Some ( filename) = filename {
101- writeln ! ( buffer, "Filename: {}" , filename) . expect ( "Failed to write to buffer" ) ;
253+ // Positional argument (file)
254+ args. files . push ( arg) ;
102255 }
103- buffer. push_str ( "Status: ERROR\n " ) ;
104- writeln ! ( buffer, "Details: {}" , error) . expect ( "Failed to write to buffer" ) ;
105- eprintln ! ( "{}" , buffer. trim( ) ) ;
106256 }
107257
108- let mut args = pico_args:: Arguments :: from_env ( ) ;
109258 let exit_code = AtomicI32 :: new ( 0 ) ;
110- if args. contains ( [ "-h" , "-- help" ] ) {
259+ if args. help {
111260 io:: stdout ( ) . write_all ( HELP_MESSAGE ) ?;
112- } else if args. contains ( [ "-v" , "-- version" ] ) {
261+ } else if args. version {
113262 io:: stdout ( ) . write_all ( VERSION_MESSAGE ) ?;
114263 } else {
115- let args = Args {
116- inline_style_tags : args
117- . opt_value_from_str ( "--inline-style-tags" ) ?
118- . unwrap_or ( true ) ,
119- keep_style_tags : args. contains ( "--keep-style-tags" ) ,
120- keep_link_tags : args. contains ( "--keep-link-tags" ) ,
121- base_url : args. opt_value_from_str ( "--base-url" ) ?,
122- extra_css : args. opt_value_from_str ( "--extra-css" ) ?,
123- output_filename_prefix : args. opt_value_from_str ( "--output-filename-prefix" ) ?,
124- load_remote_stylesheets : args. contains ( "--load-remote-stylesheets" ) ,
125- #[ cfg( feature = "stylesheet-cache" ) ]
126- cache_size : args. opt_value_from_str ( "--cache-size" ) ?,
127- files : args. free ( ) ?,
128- } ;
129- let base_url = match parse_url ( args. base_url ) {
264+ let base_url = match parse_url ( args. base_url . as_deref ( ) ) {
130265 Ok ( base_url) => base_url,
131266 Err ( error) => {
132267 format_error ( None , error) ;
133268 std:: process:: exit ( 1 ) ;
134269 }
135270 } ;
271+ #[ cfg( feature = "stylesheet-cache" ) ]
272+ let cache = if let Some ( size) = args. cache_size {
273+ if size == 0 {
274+ eprintln ! ( "ERROR: Cache size must be an integer greater than zero" ) ;
275+ std:: process:: exit ( 1 ) ;
276+ }
277+ std:: num:: NonZeroUsize :: new ( size)
278+ . map ( css_inline:: StylesheetCache :: new)
279+ . map ( std:: sync:: Mutex :: new)
280+ } else {
281+ None
282+ } ;
136283 let options = InlineOptions {
137284 inline_style_tags : args. inline_style_tags ,
138285 keep_style_tags : args. keep_style_tags ,
139286 keep_link_tags : args. keep_link_tags ,
140287 base_url,
141288 load_remote_stylesheets : args. load_remote_stylesheets ,
142289 #[ cfg( feature = "stylesheet-cache" ) ]
143- cache : {
144- if let Some ( size) = args. cache_size {
145- if size == 0 {
146- eprintln ! ( "ERROR: Cache size must be an integer greater than zero" ) ;
147- std:: process:: exit ( 1 ) ;
148- }
149-
150- std:: num:: NonZeroUsize :: new ( size)
151- . map ( css_inline:: StylesheetCache :: new)
152- . map ( std:: sync:: Mutex :: new)
153- } else {
154- None
155- }
156- } ,
290+ cache,
157291 extra_css : args. extra_css . as_deref ( ) . map ( Cow :: Borrowed ) ,
158292 preallocate_node_capacity : 32 ,
159293 resolver : Arc :: new ( DefaultStylesheetResolver ) ,
@@ -192,7 +326,7 @@ OPTIONS:
192326 } )
193327 . for_each ( |result| match result {
194328 Ok ( ( filename, result) ) => match result {
195- Ok ( _ ) => println ! ( "{filename}: SUCCESS" ) ,
329+ Ok ( ( ) ) => println ! ( "{filename}: SUCCESS" ) ,
196330 Err ( error) => {
197331 format_error ( Some ( filename. as_str ( ) ) , error) ;
198332 exit_code. store ( 1 , Ordering :: SeqCst ) ;
0 commit comments