gluesql_git_storage/
lib.rs1#![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 {}