toasty_cli/migration/
history_file.rs1use anyhow::{Result, bail};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::path::Path;
5use std::str::FromStr;
6
7const HISTORY_FILE_VERSION: u32 = 1;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct HistoryFile {
12 version: u32,
14
15 migrations: Vec<HistoryFileMigration>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct HistoryFileMigration {
21 pub id: u64,
23
24 pub name: String,
26
27 pub snapshot_name: String,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub checksum: Option<String>,
33}
34
35impl HistoryFile {
36 pub fn new() -> Self {
38 Self {
39 version: HISTORY_FILE_VERSION,
40 migrations: Vec::new(),
41 }
42 }
43
44 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
46 let contents = std::fs::read_to_string(path.as_ref())?;
47 contents.parse()
48 }
49
50 pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
52 std::fs::write(path.as_ref(), self.to_string())?;
53 Ok(())
54 }
55
56 pub fn load_or_default(path: impl AsRef<Path>) -> Result<Self> {
58 if std::fs::exists(&path)? {
59 return Self::load(path);
60 }
61 Ok(Self::default())
62 }
63
64 pub fn migrations(&self) -> &[HistoryFileMigration] {
65 &self.migrations
66 }
67
68 pub fn next_migration_number(&self) -> u32 {
70 self.migrations
71 .last()
72 .and_then(|m| {
73 m.name.split('_').next()?.parse::<u32>().ok()
75 })
76 .map(|n| n + 1)
77 .unwrap_or(0)
78 }
79
80 pub fn add_migration(&mut self, migration: HistoryFileMigration) {
82 self.migrations.push(migration);
83 }
84
85 pub fn remove_migration(&mut self, index: usize) {
87 self.migrations.remove(index);
88 }
89}
90
91impl Default for HistoryFile {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97impl FromStr for HistoryFile {
98 type Err = anyhow::Error;
99
100 fn from_str(s: &str) -> Result<Self> {
101 let file: HistoryFile = toml::from_str(s)?;
102
103 if file.version != HISTORY_FILE_VERSION {
105 bail!(
106 "Unsupported history file version: {}. Expected version {}",
107 file.version,
108 HISTORY_FILE_VERSION
109 );
110 }
111
112 Ok(file)
113 }
114}
115
116impl fmt::Display for HistoryFile {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 let toml_str = toml::to_string_pretty(self).map_err(|_| fmt::Error)?;
119 write!(f, "{}", toml_str)
120 }
121}