Skip to main content

modde_games/stardew/
saves.rs

1//! Save detection for Stardew Valley.
2
3use std::path::Path;
4
5use anyhow::{Context, Result};
6use smallvec::SmallVec;
7
8use crate::save_patterns::{CaptureSummary, PatternSaveTracker};
9use crate::traits::{DetectedSave, SaveTracker};
10
11/// [`SaveTracker`] for Stardew Valley save directories.
12pub struct StardewSaveTracker;
13
14pub static STARDEW_SAVE_TRACKER: StardewSaveTracker = StardewSaveTracker;
15
16const TRACKER: PatternSaveTracker = PatternSaveTracker {
17    prefix_rules: &[],
18    file_extensions: &[],
19    default_category: "farm",
20    recursive: false,
21    exclude_patterns: &["startup_preferences"],
22    label_extractor: |_, rel| Some(rel.to_string()),
23    summary: CaptureSummary::Default,
24};
25
26impl SaveTracker for StardewSaveTracker {
27    fn save_patterns(&self) -> SmallVec<[String; 2]> {
28        smallvec::smallvec!["*_*".into()]
29    }
30
31    fn detect_saves(&self, save_dir: &Path) -> Result<Vec<DetectedSave>> {
32        let mut saves = Vec::new();
33        if !save_dir.exists() {
34            return Ok(saves);
35        }
36        for entry in std::fs::read_dir(save_dir)
37            .with_context(|| format!("failed to read directory: {}", save_dir.display()))?
38        {
39            let entry = entry?;
40            if !entry.file_type()?.is_dir() {
41                continue;
42            }
43            let name = entry.file_name().to_string_lossy().to_string();
44            if !name.contains('_') {
45                continue;
46            }
47            let modified = entry
48                .metadata()
49                .and_then(|meta| meta.modified())
50                .unwrap_or(std::time::SystemTime::UNIX_EPOCH);
51            saves.push(DetectedSave {
52                rel_path: name.clone().into(),
53                category: std::borrow::Cow::Borrowed("farm"),
54                label: Some(name),
55                modified,
56            });
57        }
58        saves.sort_by(|a, b| b.modified.cmp(&a.modified));
59        Ok(saves)
60    }
61
62    fn exclude_patterns(&self) -> SmallVec<[String; 2]> {
63        TRACKER.exclude_patterns()
64    }
65
66    fn describe_capture(&self, saves: &[DetectedSave]) -> String {
67        TRACKER.describe_capture(saves)
68    }
69}