Skip to main content

spawn_db/
migrator.rs

1use crate::config;
2use crate::template;
3
4use anyhow::Result;
5
6static BASE_MIGRATION: &str = "BEGIN;
7
8COMMIT;
9";
10
11/// Final SQL output generator
12#[derive(Debug)]
13pub struct Migrator {
14    config: config::Config,
15    /// Name of the migration, as an object store path.
16    name: String,
17    /// Whether to use pinned components
18    use_pinned: bool,
19}
20
21impl Migrator {
22    pub fn new(config: &config::Config, name: &str, use_pinned: bool) -> Self {
23        Migrator {
24            config: config.clone(),
25            name: name.to_string(),
26            use_pinned,
27        }
28    }
29
30    /// Creates the migration folder with blank setup.
31    pub async fn create_migration(&self) -> Result<String> {
32        // TODO: return error if migration already exists.
33        let path = self.config.pather().migration_folder(&self.name);
34
35        let script_path = format!("{}/up.sql", &path);
36        println!("creating migration at {}", &script_path);
37        self.config
38            .operator()
39            .write(&script_path, BASE_MIGRATION)
40            .await?;
41
42        Ok(self.name.to_string())
43    }
44
45    /// Opens the specified script file and returns a streaming generation that can
46    /// render directly to a writer without materializing the entire SQL in memory.
47    pub async fn generate_streaming(
48        &self,
49        variables: Option<crate::variables::Variables>,
50    ) -> Result<template::StreamingGeneration> {
51        let lock_file = if self.use_pinned {
52            let path = self.config.pather().migration_lock_file_path(&self.name);
53            Some(path)
54        } else {
55            None
56        };
57        let script_path = &self.config.pather().migration_script_file_path(&self.name);
58        template::generate_streaming(&self.config, lock_file, script_path, variables).await
59    }
60}