backup_suite/smart/
types.rs

1//! AI機能の共通型定義
2//!
3//! newtype patternを使用した型安全なデータ表現を提供します。
4
5use serde::{Deserialize, Serialize};
6
7/// バックアップサイズ(バイト単位)
8///
9/// # 使用例
10///
11/// ```rust
12/// use backup_suite::smart::BackupSize;
13///
14/// let size = BackupSize::new(1_048_576); // 1 MiB
15/// assert_eq!(size.get(), 1_048_576);
16/// assert!((size.as_mb() - 1.0).abs() < 0.01); // 約1.0 MB
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
19pub struct BackupSize(u64);
20
21impl BackupSize {
22    /// 新しいBackupSizeインスタンスを作成
23    #[must_use]
24    pub const fn new(bytes: u64) -> Self {
25        Self(bytes)
26    }
27
28    /// バイト数を取得
29    #[must_use]
30    pub const fn get(self) -> u64 {
31        self.0
32    }
33
34    /// f64型として取得(統計計算用)
35    #[must_use]
36    pub const fn as_f64(self) -> f64 {
37        self.0 as f64
38    }
39
40    /// MB単位で取得
41    #[must_use]
42    pub fn as_mb(self) -> f64 {
43        self.0 as f64 / 1_048_576.0
44    }
45
46    /// GB単位で取得
47    #[must_use]
48    pub fn as_gb(self) -> f64 {
49        self.0 as f64 / 1_073_741_824.0
50    }
51}
52
53impl From<u64> for BackupSize {
54    fn from(bytes: u64) -> Self {
55        Self(bytes)
56    }
57}
58
59impl From<BackupSize> for u64 {
60    fn from(size: BackupSize) -> Self {
61        size.0
62    }
63}
64
65impl std::fmt::Display for BackupSize {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        if self.0 >= 1_073_741_824 {
68            write!(f, "{:.2} GB", self.as_gb())
69        } else if self.0 >= 1_048_576 {
70            write!(f, "{:.2} MB", self.as_mb())
71        } else if self.0 >= 1024 {
72            write!(f, "{:.2} KB", self.0 as f64 / 1024.0)
73        } else {
74            write!(f, "{} B", self.0)
75        }
76    }
77}
78
79/// 予測信頼度(0.0 - 1.0)
80///
81/// # 使用例
82///
83/// ```rust
84/// use backup_suite::smart::PredictionConfidence;
85///
86/// let confidence = PredictionConfidence::new(0.95).unwrap();
87/// assert_eq!(confidence.get(), 0.95);
88/// assert_eq!(confidence.as_percentage(), 95.0);
89/// ```
90#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
91pub struct PredictionConfidence(f64);
92
93impl PredictionConfidence {
94    /// 新しいPredictionConfidenceインスタンスを作成
95    ///
96    /// # Errors
97    ///
98    /// 値が0.0未満または1.0を超える場合はエラーを返します
99    pub fn new(value: f64) -> Result<Self, String> {
100        if !(0.0..=1.0).contains(&value) {
101            return Err(format!(
102                "信頼度は0.0から1.0の範囲である必要があります: {}",
103                value
104            ));
105        }
106        Ok(Self(value))
107    }
108
109    /// 信頼度を取得
110    #[must_use]
111    pub const fn get(self) -> f64 {
112        self.0
113    }
114
115    /// パーセンテージとして取得
116    #[must_use]
117    pub fn as_percentage(self) -> f64 {
118        self.0 * 100.0
119    }
120
121    /// 高信頼度かどうか(80%以上)
122    #[must_use]
123    pub fn is_high(self) -> bool {
124        self.0 >= 0.8
125    }
126
127    /// 中信頼度かどうか(50%-80%)
128    #[must_use]
129    pub fn is_medium(self) -> bool {
130        (0.5..0.8).contains(&self.0)
131    }
132
133    /// 低信頼度かどうか(50%未満)
134    #[must_use]
135    pub fn is_low(self) -> bool {
136        self.0 < 0.5
137    }
138}
139
140impl std::fmt::Display for PredictionConfidence {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        write!(f, "{:.1}%", self.as_percentage())
143    }
144}
145
146/// ファイル重要度(0 - 100)
147///
148/// # 使用例
149///
150/// ```rust
151/// use backup_suite::smart::FileImportance;
152///
153/// let importance = FileImportance::new(85).unwrap();
154/// assert_eq!(importance.get(), 85);
155/// assert!(importance.is_high());
156/// ```
157#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
158pub struct FileImportance(u8);
159
160impl FileImportance {
161    /// 新しいFileImportanceインスタンスを作成
162    ///
163    /// # Errors
164    ///
165    /// 値が100を超える場合はエラーを返します
166    pub fn new(value: u8) -> Result<Self, String> {
167        if value > 100 {
168            return Err(format!(
169                "重要度は0から100の範囲である必要があります: {}",
170                value
171            ));
172        }
173        Ok(Self(value))
174    }
175
176    /// 重要度を取得
177    #[must_use]
178    pub const fn get(self) -> u8 {
179        self.0
180    }
181
182    /// 高重要度かどうか(80以上)
183    #[must_use]
184    pub const fn is_high(self) -> bool {
185        self.0 >= 80
186    }
187
188    /// 中重要度かどうか(40-79)
189    #[must_use]
190    pub const fn is_medium(self) -> bool {
191        self.0 >= 40 && self.0 < 80
192    }
193
194    /// 低重要度かどうか(40未満)
195    #[must_use]
196    pub const fn is_low(self) -> bool {
197        self.0 < 40
198    }
199}
200
201impl std::fmt::Display for FileImportance {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        let label = if self.is_high() {
204            "高"
205        } else if self.is_medium() {
206            "中"
207        } else {
208            "低"
209        };
210        write!(f, "{}/100 ({})", self.0, label)
211    }
212}
213
214/// ディスク容量(バイト単位)
215///
216/// # 使用例
217///
218/// ```rust
219/// use backup_suite::smart::DiskCapacity;
220///
221/// let capacity = DiskCapacity::new(536_870_912_000); // 500 GiB
222/// assert!((capacity.as_gb() - 500.0).abs() < 0.1); // 約500 GB
223/// ```
224#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
225pub struct DiskCapacity(u64);
226
227impl DiskCapacity {
228    /// 新しいDiskCapacityインスタンスを作成
229    #[must_use]
230    pub const fn new(bytes: u64) -> Self {
231        Self(bytes)
232    }
233
234    /// バイト数を取得
235    #[must_use]
236    pub const fn get(self) -> u64 {
237        self.0
238    }
239
240    /// GB単位で取得
241    #[must_use]
242    pub fn as_gb(self) -> f64 {
243        self.0 as f64 / 1_073_741_824.0
244    }
245
246    /// TB単位で取得
247    #[must_use]
248    pub fn as_tb(self) -> f64 {
249        self.0 as f64 / 1_099_511_627_776.0
250    }
251
252    /// 使用率を計算(0.0-1.0)
253    #[must_use]
254    pub fn usage_ratio(self, used: DiskCapacity) -> f64 {
255        if self.0 == 0 {
256            return 0.0;
257        }
258        used.0 as f64 / self.0 as f64
259    }
260}
261
262impl From<u64> for DiskCapacity {
263    fn from(bytes: u64) -> Self {
264        Self(bytes)
265    }
266}
267
268impl From<DiskCapacity> for u64 {
269    fn from(capacity: DiskCapacity) -> Self {
270        capacity.0
271    }
272}
273
274impl std::fmt::Display for DiskCapacity {
275    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276        if self.0 >= 1_099_511_627_776 {
277            write!(f, "{:.2} TB", self.as_tb())
278        } else if self.0 >= 1_073_741_824 {
279            write!(f, "{:.2} GB", self.as_gb())
280        } else if self.0 >= 1_048_576 {
281            write!(f, "{:.2} MB", self.0 as f64 / 1_048_576.0)
282        } else {
283            write!(f, "{} bytes", self.0)
284        }
285    }
286}
287
288/// 失敗率(0.0 - 1.0)
289///
290/// # 使用例
291///
292/// ```rust
293/// use backup_suite::smart::FailureRate;
294///
295/// let rate = FailureRate::new(0.05).unwrap();
296/// assert_eq!(rate.get(), 0.05);
297/// assert_eq!(rate.as_percentage(), 5.0);
298/// ```
299#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
300pub struct FailureRate(f64);
301
302impl FailureRate {
303    /// 新しいFailureRateインスタンスを作成
304    ///
305    /// # Errors
306    ///
307    /// 値が0.0未満または1.0を超える場合はエラーを返します
308    pub fn new(value: f64) -> Result<Self, String> {
309        if !(0.0..=1.0).contains(&value) {
310            return Err(format!(
311                "失敗率は0.0から1.0の範囲である必要があります: {}",
312                value
313            ));
314        }
315        Ok(Self(value))
316    }
317
318    /// 失敗率を取得
319    #[must_use]
320    pub const fn get(self) -> f64 {
321        self.0
322    }
323
324    /// パーセンテージとして取得
325    #[must_use]
326    pub fn as_percentage(self) -> f64 {
327        self.0 * 100.0
328    }
329
330    /// 高リスクかどうか(20%以上)
331    #[must_use]
332    pub fn is_high_risk(self) -> bool {
333        self.0 >= 0.2
334    }
335
336    /// 中リスクかどうか(5%-20%)
337    #[must_use]
338    pub fn is_medium_risk(self) -> bool {
339        (0.05..0.2).contains(&self.0)
340    }
341
342    /// 低リスクかどうか(5%未満)
343    #[must_use]
344    pub fn is_low_risk(self) -> bool {
345        self.0 < 0.05
346    }
347}
348
349impl std::fmt::Display for FailureRate {
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351        write!(f, "{:.2}%", self.as_percentage())
352    }
353}
354
355/// 時系列データポイント
356///
357/// # 使用例
358///
359/// ```rust
360/// use backup_suite::smart::TimeSeriesPoint;
361/// use chrono::Utc;
362///
363/// let point = TimeSeriesPoint::new(Utc::now(), 1024.0);
364/// assert_eq!(point.value(), 1024.0);
365/// ```
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct TimeSeriesPoint {
368    timestamp: chrono::DateTime<chrono::Utc>,
369    value: f64,
370}
371
372impl TimeSeriesPoint {
373    /// 新しいTimeSeriesPointインスタンスを作成
374    #[must_use]
375    pub const fn new(timestamp: chrono::DateTime<chrono::Utc>, value: f64) -> Self {
376        Self { timestamp, value }
377    }
378
379    /// タイムスタンプを取得
380    #[must_use]
381    pub const fn timestamp(&self) -> &chrono::DateTime<chrono::Utc> {
382        &self.timestamp
383    }
384
385    /// 値を取得
386    #[must_use]
387    pub const fn value(&self) -> f64 {
388        self.value
389    }
390}
391
392impl std::fmt::Display for TimeSeriesPoint {
393    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394        write!(
395            f,
396            "{}: {:.2}",
397            self.timestamp.format("%Y-%m-%d %H:%M:%S"),
398            self.value
399        )
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406
407    #[test]
408    fn test_backup_size() {
409        let size = BackupSize::new(1_048_576);
410        assert_eq!(size.get(), 1_048_576);
411        assert_eq!(size.as_mb(), 1.0);
412        assert_eq!(size.as_gb(), 1.0 / 1024.0);
413    }
414
415    #[test]
416    fn test_prediction_confidence_valid() {
417        let conf = PredictionConfidence::new(0.95).unwrap();
418        assert_eq!(conf.get(), 0.95);
419        assert_eq!(conf.as_percentage(), 95.0);
420        assert!(conf.is_high());
421    }
422
423    #[test]
424    fn test_prediction_confidence_invalid() {
425        assert!(PredictionConfidence::new(-0.1).is_err());
426        assert!(PredictionConfidence::new(1.5).is_err());
427    }
428
429    #[test]
430    fn test_file_importance() {
431        let high = FileImportance::new(85).unwrap();
432        assert!(high.is_high());
433        assert!(!high.is_medium());
434
435        let medium = FileImportance::new(50).unwrap();
436        assert!(medium.is_medium());
437
438        let low = FileImportance::new(20).unwrap();
439        assert!(low.is_low());
440    }
441
442    #[test]
443    fn test_file_importance_invalid() {
444        assert!(FileImportance::new(101).is_err());
445    }
446
447    #[test]
448    fn test_disk_capacity() {
449        let capacity = DiskCapacity::new(1_073_741_824); // 1GB
450        assert_eq!(capacity.as_gb(), 1.0);
451
452        let used = DiskCapacity::new(536_870_912); // 0.5GB
453        assert_eq!(capacity.usage_ratio(used), 0.5);
454    }
455
456    #[test]
457    fn test_failure_rate() {
458        let rate = FailureRate::new(0.05).unwrap();
459        assert_eq!(rate.get(), 0.05);
460        assert_eq!(rate.as_percentage(), 5.0);
461        assert!(rate.is_medium_risk());
462    }
463
464    #[test]
465    fn test_failure_rate_invalid() {
466        assert!(FailureRate::new(-0.1).is_err());
467        assert!(FailureRate::new(1.5).is_err());
468    }
469
470    #[test]
471    fn test_time_series_point() {
472        use chrono::Utc;
473        let now = Utc::now();
474        let point = TimeSeriesPoint::new(now, 1024.0);
475        assert_eq!(point.timestamp(), &now);
476        assert_eq!(point.value(), 1024.0);
477    }
478
479    #[test]
480    fn test_backup_size_display() {
481        assert_eq!(BackupSize::new(500).to_string(), "500 B");
482        assert_eq!(BackupSize::new(2048).to_string(), "2.00 KB");
483        assert_eq!(BackupSize::new(1_048_576).to_string(), "1.00 MB");
484        assert_eq!(BackupSize::new(1_073_741_824).to_string(), "1.00 GB");
485    }
486
487    #[test]
488    fn test_prediction_confidence_display() {
489        let conf = PredictionConfidence::new(0.85).unwrap();
490        assert_eq!(conf.to_string(), "85.0%");
491    }
492
493    #[test]
494    fn test_file_importance_display() {
495        let high = FileImportance::new(90).unwrap();
496        assert_eq!(high.to_string(), "90/100 (高)");
497
498        let medium = FileImportance::new(60).unwrap();
499        assert_eq!(medium.to_string(), "60/100 (中)");
500
501        let low = FileImportance::new(20).unwrap();
502        assert_eq!(low.to_string(), "20/100 (低)");
503    }
504
505    #[test]
506    fn test_disk_capacity_display() {
507        assert_eq!(DiskCapacity::new(500).to_string(), "500 bytes");
508        assert_eq!(DiskCapacity::new(1_048_576).to_string(), "1.00 MB");
509        assert_eq!(DiskCapacity::new(1_073_741_824).to_string(), "1.00 GB");
510        assert_eq!(DiskCapacity::new(1_099_511_627_776).to_string(), "1.00 TB");
511    }
512
513    #[test]
514    fn test_failure_rate_display() {
515        let rate = FailureRate::new(0.15).unwrap();
516        assert_eq!(rate.to_string(), "15.00%");
517    }
518
519    #[test]
520    fn test_time_series_point_display() {
521        use chrono::Utc;
522        let point = TimeSeriesPoint::new(Utc::now(), 123.456);
523        let display = point.to_string();
524        assert!(display.contains("123.46"));
525    }
526}