gluesql_git_storage/
lib.rs

1#![deny(clippy::str_to_string)]
2
3mod command_ext;
4mod store;
5mod store_mut;
6
7pub use command_ext::CommandExt;
8use {
9    gluesql_core::{
10        error::{Error, Result},
11        store::{
12            AlterTable, CustomFunction, CustomFunctionMut, Index, IndexMut, Metadata, Store,
13            StoreMut, Transaction,
14        },
15    },
16    gluesql_csv_storage::CsvStorage,
17    gluesql_file_storage::FileStorage,
18    gluesql_json_storage::JsonStorage,
19    std::{
20        path::{Path, PathBuf},
21        process::Command,
22    },
23    strum_macros::Display,
24};
25
26pub struct GitStorage {
27    pub storage_base: StorageBase,
28    pub path: PathBuf,
29    pub remote: String,
30    pub branch: String,
31}
32
33pub enum StorageBase {
34    File(FileStorage),
35    Csv(CsvStorage),
36    Json(JsonStorage),
37}
38
39#[derive(Clone, Copy, Display)]
40#[strum(serialize_all = "lowercase")]
41pub enum StorageType {
42    File,
43    Csv,
44    Json,
45}
46
47const DEFAULT_REMOTE: &str = "origin";
48const DEFAULT_BRANCH: &str = "main";
49
50impl GitStorage {
51    pub fn init<T: AsRef<Path>>(path: T, storage_type: StorageType) -> Result<Self> {
52        let path = path.as_ref();
53        let storage_base = Self::storage_base(path, storage_type)?;
54        Command::new("git")
55            .current_dir(path)
56            .arg("init")
57            .execute()?;
58
59        Ok(Self {
60            storage_base,
61            path: path.to_owned(),
62            remote: DEFAULT_REMOTE.to_owned(),
63            branch: DEFAULT_BRANCH.to_owned(),
64        })
65    }
66
67    pub fn open<T: AsRef<Path>>(path: T, storage_type: StorageType) -> Result<Self> {
68        let path = path.as_ref();
69        let storage_base = Self::storage_base(path, storage_type)?;
70
71        Ok(Self {
72            storage_base,
73            path: path.to_owned(),
74            remote: DEFAULT_REMOTE.to_owned(),
75            branch: DEFAULT_BRANCH.to_owned(),
76        })
77    }
78
79    fn storage_base(path: &Path, storage_type: StorageType) -> Result<StorageBase> {
80        use StorageType::*;
81
82        match storage_type {
83            File => FileStorage::new(path).map(StorageBase::File),
84            Csv => CsvStorage::new(path).map(StorageBase::Csv),
85            Json => JsonStorage::new(path).map(StorageBase::Json),
86        }
87    }
88
89    pub fn set_remote(&mut self, remote: String) {
90        self.remote = remote;
91    }
92
93    pub fn set_branch(&mut self, branch: String) {
94        self.branch = branch;
95    }
96
97    pub fn add_and_commit(&self, message: &str) -> Result<()> {
98        Command::new("git")
99            .current_dir(&self.path)
100            .arg("add")
101            .arg(".")
102            .execute()?;
103
104        Command::new("git")
105            .current_dir(&self.path)
106            .arg("commit")
107            .arg("--allow-empty")
108            .arg("-m")
109            .arg(message)
110            .execute()
111    }
112
113    pub fn pull(&self) -> Result<()> {
114        Command::new("git")
115            .current_dir(&self.path)
116            .arg("pull")
117            .arg(&self.remote)
118            .arg(&self.branch)
119            .execute()
120    }
121
122    pub fn push(&self) -> Result<()> {
123        Command::new("git")
124            .current_dir(&self.path)
125            .arg("push")
126            .arg(&self.remote)
127            .arg(&self.branch)
128            .execute()
129    }
130
131    fn get_store(&self) -> &dyn Store {
132        match &self.storage_base {
133            StorageBase::File(storage) => storage,
134            StorageBase::Csv(storage) => storage,
135            StorageBase::Json(storage) => storage,
136        }
137    }
138
139    fn get_store_mut(&mut self) -> &mut dyn StoreMut {
140        match &mut self.storage_base {
141            StorageBase::File(storage) => storage,
142            StorageBase::Csv(storage) => storage,
143            StorageBase::Json(storage) => storage,
144        }
145    }
146}
147
148pub trait ResultExt<T, E: ToString> {
149    fn map_storage_err(self) -> Result<T, Error>;
150}
151
152impl<T, E: ToString> ResultExt<T, E> for std::result::Result<T, E> {
153    fn map_storage_err(self) -> Result<T, Error> {
154        self.map_err(|e| e.to_string()).map_err(Error::StorageMsg)
155    }
156}
157
158impl AlterTable for GitStorage {}
159impl Index for GitStorage {}
160impl IndexMut for GitStorage {}
161impl Transaction for GitStorage {}
162impl Metadata for GitStorage {}
163impl CustomFunction for GitStorage {}
164impl CustomFunctionMut for GitStorage {}