Skip to content

Commit 3db4484

Browse files
committed
feat: move sprite validation + unclutter
1 parent 0b46368 commit 3db4484

File tree

1 file changed

+76
-106
lines changed
  • martin-core/src/resources/sprites

1 file changed

+76
-106
lines changed

martin-core/src/resources/sprites/mod.rs

Lines changed: 76 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -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

333303
impl SpriteSources {

0 commit comments

Comments
 (0)