@@ -72,63 +72,23 @@ impl SpriteSources {
7272 /// Adds a sprite source directory containing SVG files.
7373 /// Files are ignored - only directories accepted. Duplicates ignored with warning.
7474 /// Performs basic validation of SVG format before adding the source.
75- pub fn add_source ( & mut self , id : String , path : PathBuf ) {
75+ pub fn add_source ( & mut self , id : String , path : PathBuf ) -> Result < ( ) , SpriteError > {
7676 let disp_path = path. display ( ) ;
7777
7878 if path. is_file ( ) {
7979 warn ! ( "Ignoring non-directory sprite source {id} from {disp_path}" ) ;
80- return ;
80+ return Err ( SpriteError :: DirectoryValidationFailed (
81+ path,
82+ "Path is not a directory" . to_string ( ) ,
83+ ) ) ;
8184 }
85+
8286 if !path. exists ( ) {
8387 warn ! ( "Sprite source {id} path doesn't exist: {disp_path}" ) ;
84- return ;
85- }
86-
87- match std:: fs:: read_dir ( & path) {
88- Ok ( entries) => {
89- let mut file_count = 0 ;
90- let mut svg_count = 0 ;
91- let mut sprite_output_files = Vec :: new ( ) ;
92-
93- for entry in entries. flatten ( ) {
94- let entry_path = entry. path ( ) ;
95- if entry_path. is_file ( ) {
96- file_count += 1 ;
97- if let Some ( extension) = entry_path. extension ( ) {
98- let ext = extension. to_string_lossy ( ) . to_lowercase ( ) ;
99- if ext == "svg" {
100- svg_count += 1 ;
101- } else if ext == "png" || ext == "json" {
102- let filename = entry_path. file_stem ( )
103- . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
104- . unwrap_or_default ( ) ;
105- if filename. contains ( "sprite" ) || filename. contains ( "@2x" ) {
106- sprite_output_files. push ( entry_path. file_name ( )
107- . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
108- . unwrap_or_default ( ) ) ;
109- }
110- }
111- }
112- }
113- }
114-
115-
116- if file_count == 0 {
117- warn ! ( "Sprite source {id} directory is empty: {disp_path}" ) ;
118- } else if svg_count == 0 && !sprite_output_files. is_empty ( ) {
119- warn ! (
120- "Sprite source {id} contains files ({}) but no valid SVG sources: {disp_path}. \
121- Martin requires source SVG files.",
122- sprite_output_files. join( ", " )
123- ) ;
124- } else if svg_count == 0 {
125- warn ! ( "Sprite source {id} contains no SVG files: {disp_path}" ) ;
126- }
127- }
128- Err ( e) => {
129- warn ! ( "Cannot read sprite source {id} directory {disp_path}: {e}" ) ;
130- return ;
131- }
88+ return Err ( SpriteError :: DirectoryValidationFailed (
89+ path,
90+ "Path does not exist" . to_string ( ) ,
91+ ) ) ;
13292 }
13393
13494 match self . 0 . entry ( id) {
@@ -144,8 +104,10 @@ impl SpriteSources {
144104 v. insert ( SpriteSource { path } ) ;
145105 }
146106 }
107+
108+ Ok ( ( ) )
147109 }
148-
110+
149111 /// Validates a sprite source directory to ensure it contains valid SVG files.
150112 /// Checks include:
151113 /// - Directory existence and accessibility
@@ -162,38 +124,22 @@ impl SpriteSources {
162124 warn ! ( "Sprite source is not a directory: {disp_path}" ) ;
163125 return Err ( SpriteError :: DirectoryValidationFailed (
164126 path. clone ( ) ,
165- "Path is not a directory" . to_string ( )
127+ "Path is not a directory" . to_string ( ) ,
166128 ) ) ;
167129 }
168130
169- // Check directory permissions by trying to read it
170- let mut entries = tokio:: fs:: read_dir ( path) . await . map_err ( on_err) ?;
171- let mut svg_count = 0 ;
172- let mut total_files = 0 ;
173- let mut sprite_output_files = Vec :: new ( ) ; // Track pre-generated sprite files
131+ let ( total_files, svg_count, sprite_output_files) =
132+ Self :: scan_directory_files ( path) . await ?;
174133
134+ let mut entries = tokio:: fs:: read_dir ( path) . await . map_err ( on_err) ?;
175135 while let Some ( entry) = entries. next_entry ( ) . await . map_err ( on_err) ? {
176136 let entry_path = entry. path ( ) ;
177- total_files += 1 ;
178-
179137 if entry_path. is_file ( ) {
180138 if let Some ( extension) = entry_path. extension ( ) {
181- let ext = extension. to_string_lossy ( ) . to_lowercase ( ) ;
182- if ext == "svg" {
183- svg_count += 1 ;
184- // Validate individual SVG file
139+ if extension. to_string_lossy ( ) . to_lowercase ( ) == "svg" {
185140 if let Err ( e) = self . validate_svg_file ( & entry_path) . await {
186141 warn ! ( "Invalid SVG file {}: {}" , entry_path. display( ) , e) ;
187142 }
188- } else if ext == "png" || ext == "json" {
189- let filename = entry_path. file_stem ( )
190- . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
191- . unwrap_or_default ( ) ;
192- if filename. contains ( "sprite" ) || filename. contains ( "@2x" ) {
193- sprite_output_files. push ( entry_path. file_name ( )
194- . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
195- . unwrap_or_default ( ) ) ;
196- }
197143 }
198144 }
199145 }
@@ -204,26 +150,26 @@ impl SpriteSources {
204150 return Err ( SpriteError :: EmptyDirectory ( path. clone ( ) ) ) ;
205151 }
206152
207- if svg_count == 0 &&
208- sprite_output_files. is_empty ( ) {
209- warn ! (
210- "No SVG source files found in sprite directory: {disp_path}. \
153+ if svg_count == 0 && sprite_output_files. is_empty ( ) {
154+ warn ! (
155+ "No SVG source files found in sprite directory: {disp_path}. \
211156 Found pre-generated sprite files: {}. \
212157 Martin requires source SVG files, not pre-generated sprite outputs.",
213- sprite_output_files. join( ", " )
214- ) ;
215- return Err ( SpriteError :: DirectoryValidationFailed (
216- path. clone ( ) ,
217- format ! (
218- "Directory contains pre-generated sprite files ({}) but no source SVG files. \
158+ sprite_output_files. join( ", " )
159+ ) ;
160+ return Err ( SpriteError :: DirectoryValidationFailed (
161+ path. clone ( ) ,
162+ format ! (
163+ "Directory contains pre-generated sprite files ({}) but no source SVG files. \
219164 Please provide a directory with .svg files instead.",
220- sprite_output_files. join( ", " )
221- )
222- ) ) ;
223- }
224-
165+ sprite_output_files. join( ", " )
166+ ) ,
167+ ) ) ;
168+ }
225169
226- info ! ( "Validated sprite directory {disp_path}: found {svg_count} SVG files out of {total_files} total files" ) ;
170+ info ! (
171+ "Validated sprite directory {disp_path}: found {svg_count} SVG files out of {total_files} total files"
172+ ) ;
227173 Ok ( ( ) )
228174 }
229175
@@ -237,22 +183,22 @@ impl SpriteSources {
237183 if content. is_empty ( ) {
238184 return Err ( SpriteError :: InvalidSvgFormat (
239185 path. clone ( ) ,
240- "File is empty" . to_string ( )
186+ "File is empty" . to_string ( ) ,
241187 ) ) ;
242188 }
243189
244190 if !content. starts_with ( "<?xml" ) && !content. starts_with ( "<svg" ) {
245191 return Err ( SpriteError :: InvalidSvgFormat (
246192 path. clone ( ) ,
247- "Missing SVG or XML declaration" . to_string ( )
193+ "Missing SVG or XML declaration" . to_string ( ) ,
248194 ) ) ;
249195 }
250196
251197 // Check if it contains an SVG tag
252198 if !content. contains ( "<svg" ) {
253199 return Err ( SpriteError :: InvalidSvgFormat (
254200 path. clone ( ) ,
255- "No SVG element found" . to_string ( )
201+ "No SVG element found" . to_string ( ) ,
256202 ) ) ;
257203 }
258204
@@ -281,7 +227,7 @@ impl SpriteSources {
281227 match self . validate_source_directory ( path) . await {
282228 Ok ( ( ) ) => {
283229 valid_sources += 1 ;
284- if let Ok ( svg_count) = count_svg_files ( path) . await {
230+ if let Ok ( svg_count) = Self :: count_svg_files ( path) . await {
285231 total_svg_files += svg_count;
286232 }
287233 }
@@ -308,26 +254,50 @@ impl SpriteSources {
308254
309255 Ok ( ( ) )
310256 }
311- }
312257
313- /// Helper function to count SVG files in a directory.
314- async fn count_svg_files ( path : & PathBuf ) -> Result < usize , SpriteError > {
315- let on_err = |e| SpriteError :: IoError ( e, path. clone ( ) ) ;
316- let mut entries = tokio:: fs:: read_dir ( path) . await . map_err ( on_err) ?;
317- let mut count = 0 ;
318-
319- while let Some ( entry) = entries. next_entry ( ) . await . map_err ( on_err) ? {
320- let entry_path = entry. path ( ) ;
321- if entry_path. is_file ( ) {
322- if let Some ( extension) = entry_path. extension ( ) {
323- if extension. to_string_lossy ( ) . to_lowercase ( ) == "svg" {
324- count += 1 ;
258+ /// Scans a directory and returns file counts.
259+ async fn scan_directory_files (
260+ path : & PathBuf ,
261+ ) -> Result < ( usize , usize , Vec < String > ) , SpriteError > {
262+ let on_err = |e| SpriteError :: IoError ( e, path. clone ( ) ) ;
263+ let mut entries = tokio:: fs:: read_dir ( path) . await . map_err ( on_err) ?;
264+ let mut total_files = 0 ;
265+ let mut svg_count = 0 ;
266+ let mut sprite_output_files = Vec :: new ( ) ;
267+
268+ while let Some ( entry) = entries. next_entry ( ) . await . map_err ( on_err) ? {
269+ let entry_path = entry. path ( ) ;
270+ if entry_path. is_file ( ) {
271+ total_files += 1 ;
272+ if let Some ( extension) = entry_path. extension ( ) {
273+ let ext = extension. to_string_lossy ( ) . to_lowercase ( ) ;
274+ if ext == "svg" {
275+ svg_count += 1 ;
276+ } else if ext == "png" || ext == "json" {
277+ let filename = entry_path
278+ . file_stem ( )
279+ . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
280+ . unwrap_or_default ( ) ;
281+ if filename. contains ( "sprite" ) || filename. contains ( "@2x" ) {
282+ sprite_output_files. push (
283+ entry_path
284+ . file_name ( )
285+ . map ( |s| s. to_string_lossy ( ) . to_string ( ) )
286+ . unwrap_or_default ( ) ,
287+ ) ;
288+ }
289+ }
325290 }
326291 }
327292 }
293+
294+ Ok ( ( total_files, svg_count, sprite_output_files) )
328295 }
329296
330- Ok ( count)
297+ async fn count_svg_files ( path : & PathBuf ) -> Result < usize , SpriteError > {
298+ let ( _, svg_count, _) = Self :: scan_directory_files ( path) . await ?;
299+ Ok ( svg_count)
300+ }
331301}
332302
333303impl SpriteSources {
0 commit comments