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
use std::{path::Path, fs::{read_to_string, self}, io::ErrorKind};

use anyhow::{Context, anyhow};
use log::info;
use serde::{Serialize, Deserialize};
use toml_edit::{Document, value, array, Table};

use crate::salmo_contex::SalmoContext;

use super::migrations::Migration;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommittedFile {
  pub commits: Vec<Commit>
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Commit {
  pub id: String,
  /// the hex encoded string of the hash
  pub hash: String
}

impl CommittedFile {
  pub fn load(dir: &Path) -> anyhow::Result<Self> {
    let file = match read_to_string(dir.join("committed.toml")) {
        Ok(s) => s,
        Err(e) => match e.kind() {
            ErrorKind::NotFound => {
              info!("No committed file. Assuming no commits, this is fine.");
              return Ok(CommittedFile { commits: Vec::new() })
            },
            _ => return Err(e.into()),
        },
    };

    toml_edit::easy::from_str(&file).context("parsing committed.toml")
  }

  pub fn add_commit(ctx: &SalmoContext, migration_id: &str) -> anyhow::Result<Commit> {
    let dir = ctx.config.migrations_directory.join("committed.toml");
    let file = read_to_string(&dir).or_else(|e| match e.kind() {
      ErrorKind::NotFound => Ok("".to_string()),
      _ => Err(e)
    })?;
    let mut doc = file.parse::<Document>()?;
    doc.entry("version").or_insert(value("1"));
    let commits = doc.entry("commits")
      .or_insert(array())
      .as_array_of_tables_mut()
      .ok_or_else(|| anyhow!("corrupt committed.toml: the [commits] value must be an array of tables"))?;

    let migrations = ctx.migrations()?;
    let m = migrations.db.get(migration_id).ok_or_else(|| anyhow!("migration with id `{}` does not exist!", migration_id))?;
    let hash = m.migrate_hash()?;

    let mut commit_table = Table::new();
    commit_table.insert("id", value(migration_id));
    commit_table.insert("hash", value(&hash));

    commits.push(commit_table);
    fs::write(dir, doc.to_string())?;

    Ok(Commit { id: migration_id.to_string(), hash })
  }

  pub fn contains_migration(&self, m: &Migration) -> bool {
    self.commits.iter().any(|c| c.id == m.id )
  }
}