File size: 5,931 Bytes
343eed9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//! Module d'optimisation pour ressources limitées
//! Stratégie: Single-threaded, streaming, minimal memory footprint

use std::path::{Path, PathBuf};
use crate::error::Result;

/// Configuration pour optimisation mémoire
#[derive(Debug, Clone, Copy)]
pub struct MemoryOptimization {
    /// Vider le cache après chaque image (vs garder en mémoire)
    pub aggressive_cleanup: bool,
    /// Taille max du cache d'images (0 = aucun cache)
    pub max_cache_mb: usize,
    /// Réduire la qualité si pas assez de RAM
    pub adaptive_quality: bool,
    /// Utiliser les fichiers temporaires plutôt que la RAM
    pub use_temp_files: bool,
}

impl Default for MemoryOptimization {
    fn default() -> Self {
        Self {
            aggressive_cleanup: true,
            max_cache_mb: 100,
            adaptive_quality: true,
            use_temp_files: true,
        }
    }
}

/// Détecteur de ressources disponibles
pub struct ResourceMonitor {
    pub optimization: MemoryOptimization,
}

impl ResourceMonitor {
    pub fn new() -> Self {
        Self {
            optimization: MemoryOptimization::default(),
        }
    }

    /// Vérifier la RAM disponible (estimation)
    pub fn get_available_memory_mb() -> usize {
        // Estimation simple: on suppose ~500MB dispo pour l'app
        // En production, utiliser sysinfo crate
        500
    }

    /// Ajuster les paramètres selon les ressources
    pub fn auto_configure() -> MemoryOptimization {
        let available = Self::get_available_memory_mb();

        MemoryOptimization {
            aggressive_cleanup: available < 1000,
            max_cache_mb: (available / 5).min(200),
            adaptive_quality: available < 2000,
            use_temp_files: available < 1500,
        }
    }

    /// Nettoyer les ressources
    pub fn cleanup(temp_dir: &Path) -> std::io::Result<()> {
        if temp_dir.exists() {
            std::fs::remove_dir_all(temp_dir)?;
        }
        Ok(())
    }
}

/// Cache avec limite de taille
pub struct LimitedCache {
    max_size_mb: usize,
    current_size_mb: usize,
}

impl LimitedCache {
    pub fn new(max_size_mb: usize) -> Self {
        Self {
            max_size_mb,
            current_size_mb: 0,
        }
    }

    pub fn can_cache(&self, size_mb: usize) -> bool {
        self.current_size_mb + size_mb <= self.max_size_mb
    }

    pub fn add(&mut self, size_mb: usize) {
        self.current_size_mb += size_mb;
    }

    pub fn clear(&mut self) {
        self.current_size_mb = 0;
    }
}

/// Gestionnaire de génération en streaming pour traiter images une à une
pub struct StreamingGenerator {
    pub temp_dir: PathBuf,
    pub use_temp: bool,
}

impl StreamingGenerator {
    pub fn new(work_dir: &Path, use_temp: bool) -> Result<Self> {
        let temp_dir = work_dir.join(".temp");
        if use_temp && !temp_dir.exists() {
            std::fs::create_dir_all(&temp_dir)?;
        }
        Ok(Self { temp_dir, use_temp })
    }

    /// Traiter une image en streaming (charge -> traite -> décharge)
    pub async fn process_image_streaming<F>(
        &self,
        input: &Path,
        output: &Path,
        processor: F,
    ) -> Result<()>
    where
        F: Fn(image::DynamicImage) -> Result<image::DynamicImage>,
    {
        // 1. Charger
        let img = image::open(input).map_err(|e| {
            crate::error::GeneratorError::ImageGenerationFailed(format!("Load failed: {}", e))
        })?;

        // 2. Traiter (reste en mémoire)
        let processed = processor(img)?;

        // 3. Sauvegarder
        processed.save(output).map_err(|e| {
            crate::error::GeneratorError::ImageGenerationFailed(format!("Save failed: {}", e))
        })?;

        // 4. Nettoyer explicitement
        drop(processed);

        Ok(())
    }

    /// Cleanup
    pub fn cleanup(&self) -> std::io::Result<()> {
        if self.use_temp && self.temp_dir.exists() {
            std::fs::remove_dir_all(&self.temp_dir)?;
        }
        Ok(())
    }
}

/// Checkpointing pour reprendre depuis le dernier succès
pub struct CheckpointManager {
    pub checkpoint_file: PathBuf,
}

impl CheckpointManager {
    pub fn new(work_dir: &Path) -> Self {
        Self {
            checkpoint_file: work_dir.join(".generation_checkpoint.json"),
        }
    }

    /// Sauvegarder le progrès
    pub fn save_checkpoint(&self, story_id: &str, scene_id: usize) -> Result<()> {
        #[derive(serde::Serialize)]
        struct Checkpoint {
            story_id: String,
            last_scene: usize,
        }

        let checkpoint = Checkpoint {
            story_id: story_id.to_string(),
            last_scene: scene_id,
        };

        let json = serde_json::to_string(&checkpoint)?;
        std::fs::write(&self.checkpoint_file, json)?;
        Ok(())
    }

    /// Charger le dernier checkpoint
    pub fn load_checkpoint(&self) -> Result<Option<(String, usize)>> {
        if !self.checkpoint_file.exists() {
            return Ok(None);
        }

        #[derive(serde::Deserialize)]
        struct Checkpoint {
            story_id: String,
            last_scene: usize,
        }

        let json = std::fs::read_to_string(&self.checkpoint_file)?;
        let checkpoint: Checkpoint = serde_json::from_str(&json)?;
        Ok(Some((checkpoint.story_id, checkpoint.last_scene)))
    }

    /// Nettoyer après succès
    pub fn clear(&self) -> std::io::Result<()> {
        if self.checkpoint_file.exists() {
            std::fs::remove_file(&self.checkpoint_file)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_limited_cache() {
        let mut cache = LimitedCache::new(100);
        assert!(cache.can_cache(50));
        cache.add(50);
        assert!(cache.can_cache(49));
        assert!(!cache.can_cache(51));
        cache.clear();
        assert!(cache.can_cache(100));
    }
}