11use std:: {
2+ cell:: RefCell ,
23 ffi:: OsStr ,
34 io:: { IsTerminal , Read , Write } ,
45 path:: PathBuf ,
56 str:: FromStr ,
67} ;
78
89use anyhow:: anyhow;
10+ use ignore:: { WalkBuilder , WalkState } ;
911use oxvg_ast:: {
1012 implementations:: markup5ever:: { Element5Ever , Node5Ever } ,
1113 visitor:: Info ,
1214} ;
1315use oxvg_optimiser:: Jobs ;
14- use walkdir:: WalkDir ;
1516
1617use crate :: { args:: RunCommand , config:: Config } ;
1718
@@ -29,8 +30,15 @@ pub struct Optimise {
2930 /// If no config is specified the current config will be printed instead.
3031 #[ clap( long, short, num_args( 0 ..=1 ) ) ]
3132 pub config : Option < Vec < PathBuf > > ,
33+ /// If the path is a directory, whether to walk through and optimise it's subdirectories
3234 #[ clap( long, short, default_value = "false" ) ]
3335 pub recursive : bool ,
36+ /// Search through hidden files and directories
37+ #[ clap( long, short = '.' , default_value = "false" ) ]
38+ pub hidden : bool ,
39+ /// Sets the approximate number of threads to use. A value of 0 (default) will automatically determine the appropriate number
40+ #[ clap( long, short, default_value = "0" ) ]
41+ pub threads : usize ,
3442}
3543
3644impl RunCommand for Optimise {
@@ -39,9 +47,11 @@ impl RunCommand for Optimise {
3947 let Some ( config) = config else {
4048 return Ok ( ( ) ) ;
4149 } ;
42- let jobs = config. optimise . unwrap_or_default ( ) ;
50+ if let Some ( jobs) = config. optimise {
51+ LOADED_JOBS . set ( jobs) ;
52+ }
4353
44- self . handle_paths ( & jobs )
54+ self . handle_paths ( )
4555 }
4656}
4757
@@ -52,7 +62,7 @@ impl Optimise {
5262 dom. serialize_into ( wr)
5363 }
5464
55- fn handle_stdin ( & self , jobs : & Jobs < Element5Ever > ) -> anyhow:: Result < ( ) > {
65+ fn handle_stdin ( & self , jobs : Jobs < Element5Ever > ) -> anyhow:: Result < ( ) > {
5666 use oxvg_ast:: parse:: Node ;
5767
5868 let mut string = String :: new ( ) ;
@@ -63,7 +73,7 @@ impl Optimise {
6373 path : None ,
6474 multipass_count : 0 ,
6575 } ;
66- jobs. clone ( ) . run ( & dom, & info) ?;
76+ jobs. run ( & dom, & info) ?;
6777
6878 if let Some ( output) = & self . output . as_ref ( ) . and_then ( |o| {
6979 eprintln ! ( "Warning: Using empty `-o,--output` with stdin will print to stdout, you can instead omit `-o,--output`." ) ;
@@ -124,7 +134,7 @@ impl Optimise {
124134 }
125135 }
126136
127- fn handle_path ( & self , jobs : & Jobs < Element5Ever > , path : & PathBuf ) -> anyhow :: Result < ( ) > {
137+ fn handle_path ( & self , path : & PathBuf ) {
128138 let output_path = |input : & PathBuf | {
129139 let Some ( output) = self . output . as_ref ( ) else {
130140 return Ok ( None ) ;
@@ -134,34 +144,44 @@ impl Optimise {
134144 } ;
135145 input. strip_prefix ( path) . map ( |p| Some ( output. join ( p) ) )
136146 } ;
137- let mut walker = WalkDir :: new ( path) ;
138- if !self . recursive {
139- walker = walker. max_depth ( 1 ) ;
140- }
141- for file in walker. follow_links ( true ) {
142- let file = file?;
143- if !file. file_type ( ) . is_file ( ) {
144- continue ;
145- }
146- let path = file. into_path ( ) ;
147- if path. extension ( ) . and_then ( OsStr :: to_str) != Some ( "svg" ) {
148- continue ;
149- }
150- Self :: handle_file ( jobs, & path, output_path ( & path) ?. as_ref ( ) ) ?;
151- }
152-
153- Ok ( ( ) )
147+ WalkBuilder :: new ( path)
148+ . max_depth ( if self . recursive { None } else { Some ( 1 ) } )
149+ . follow_links ( true )
150+ . threads ( self . threads )
151+ . build_parallel ( )
152+ . run ( || {
153+ Box :: new ( move |path| {
154+ let jobs = LOADED_JOBS . with_borrow ( Clone :: clone) ;
155+ let Ok ( path) = path else {
156+ return WalkState :: Continue ;
157+ } ;
158+ if !path. file_type ( ) . is_some_and ( |f| f. is_file ( ) ) {
159+ return WalkState :: Continue ;
160+ }
161+ let path = path. into_path ( ) ;
162+ if path. extension ( ) . and_then ( OsStr :: to_str) != Some ( "svg" ) {
163+ return WalkState :: Continue ;
164+ }
165+ let Ok ( output_path) = output_path ( & path) else {
166+ return WalkState :: Continue ;
167+ } ;
168+ if let Err ( err) = Self :: handle_file ( & jobs, & path, output_path. as_ref ( ) ) {
169+ eprintln ! ( "{err}" ) ;
170+ } ;
171+ WalkState :: Continue
172+ } )
173+ } ) ;
154174 }
155175
156- fn handle_paths ( & self , jobs : & Jobs < Element5Ever > ) -> anyhow:: Result < ( ) > {
176+ fn handle_paths ( & self ) -> anyhow:: Result < ( ) > {
157177 if !std:: io:: stdin ( ) . is_terminal ( )
158178 && self . paths . len ( ) <= 1
159179 && self
160180 . paths
161181 . first ( )
162182 . is_none_or ( |path| path == & PathBuf :: from_str ( "." ) . unwrap ( ) )
163183 {
164- return self . handle_stdin ( jobs) ;
184+ return LOADED_JOBS . with ( |jobs| self . handle_stdin ( jobs. take ( ) ) ) ;
165185 }
166186 if self . paths . is_empty ( ) {
167187 return Err ( anyhow ! (
@@ -170,7 +190,7 @@ impl Optimise {
170190 }
171191
172192 for path in & self . paths {
173- self . handle_path ( jobs , path) ? ;
193+ self . handle_path ( path) ;
174194 }
175195 Ok ( ( ) )
176196 }
@@ -199,3 +219,7 @@ impl Optimise {
199219 }
200220 }
201221}
222+
223+ thread_local ! {
224+ static LOADED_JOBS : RefCell <Jobs <Element5Ever >> = RefCell :: new( Jobs :: default ( ) ) ;
225+ }
0 commit comments