From f193e13972fd6b4f15ee981c24a6099223b3e00f Mon Sep 17 00:00:00 2001
From: Pavel Kirilin <win10@list.ru>
Date: Sat, 18 Dec 2021 17:49:45 +0400
Subject: [PATCH] Separated info storage and file storage.

Signed-off-by: Pavel Kirilin <win10@list.ru>
---
 src/config.rs                          |  41 +++++++--
 src/errors.rs                          |   4 +-
 src/info_storages/file_info.rs         |  57 ++++++++++++
 src/info_storages/file_info_storage.rs |  87 ++++++++++++++++++
 src/info_storages/mod.rs               |  53 +++++++++++
 src/main.rs                            |  13 +--
 src/storages/file_storage.rs           |  77 ++++------------
 src/storages/mod.rs                    |  86 +++---------------
 src/storages/sqlite_file_storage.rs    | 119 -------------------------
 9 files changed, 272 insertions(+), 265 deletions(-)
 create mode 100644 src/info_storages/file_info.rs
 create mode 100644 src/info_storages/file_info_storage.rs
 create mode 100644 src/info_storages/mod.rs
 delete mode 100644 src/storages/sqlite_file_storage.rs

diff --git a/src/config.rs b/src/config.rs
index f0fddef..9609d3a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -3,11 +3,15 @@ use std::path::PathBuf;
 use structopt::StructOpt;
 
 use crate::errors::RustusError;
+use crate::info_storages::AvailableInfoStores;
 use crate::storages::AvailableStores;
 
 #[derive(StructOpt, Debug, Clone)]
 pub struct StorageOptions {
     /// Rustus storage type.
+    ///
+    /// Storages are used to store
+    /// uploads.
     #[structopt(long, short, default_value = "file_storage", env = "RUSTUS_STORAGE")]
     pub storage: AvailableStores,
 
@@ -21,18 +25,38 @@ pub struct StorageOptions {
         required_if("storage", "file_storage"),
         required_if("storage", "sqlite_file_storage")
     )]
-    pub data: PathBuf,
+    pub data_dir: PathBuf,
+}
 
-    /// Path to SQLite file.
+#[derive(StructOpt, Debug, Clone)]
+pub struct InfoStoreOptions {
+    /// Type of info storage.
+    ///
+    /// Info storages are used
+    /// to store information about
+    /// uploads.
     ///
-    /// This file is used to
-    /// store information about uploaded files.
+    /// This information is used in
+    /// HEAD requests.
     #[structopt(
         long,
-        default_value = "data/info.sqlite3",
-        required_if("storage", "sqlite_file_storage")
+        short,
+        default_value = "file_info_storage",
+        env = "RUSTUS_INFO_STORAGE"
     )]
-    pub sqlite_dsn: PathBuf,
+    pub info_storage: AvailableInfoStores,
+
+    /// Rustus info directory
+    ///
+    /// This directory is used to store .info files
+    /// for `file_info_storage`.
+    #[structopt(
+        long,
+        default_value = "./data",
+        required_if("info_storage", "file_info_storage"),
+        env = "RUSTUS_INFO_DIR"
+    )]
+    pub info_dir: PathBuf,
 }
 
 #[derive(Debug, StructOpt, Clone)]
@@ -79,6 +103,9 @@ pub struct RustusConf {
 
     #[structopt(flatten)]
     pub storage_opts: StorageOptions,
+
+    #[structopt(flatten)]
+    pub info_storage_opts: InfoStoreOptions,
 }
 
 /// Enum of available Protocol Extensions
diff --git a/src/errors.rs b/src/errors.rs
index cc26935..ba591c1 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,8 +1,8 @@
 use std::io::{Error, ErrorKind};
 
+use actix_web::{HttpResponse, ResponseError};
 use actix_web::dev::HttpResponseBuilder;
 use actix_web::http::StatusCode;
-use actix_web::{HttpResponse, ResponseError};
 
 pub type RustusResult<T> = Result<T, RustusError>;
 
@@ -26,6 +26,8 @@ pub enum RustusError {
     UnableToWrite(String),
     #[error("Unable to remove file {0}")]
     UnableToRemove(String),
+    #[error("Unable to prepare info storage. Reason: {0}")]
+    UnableToPrepareInfoStorage(String),
     #[error("Unable to prepare storage. Reason: {0}")]
     UnableToPrepareStorage(String),
     #[error("Unknown extension: {0}")]
diff --git a/src/info_storages/file_info.rs b/src/info_storages/file_info.rs
new file mode 100644
index 0000000..208343b
--- /dev/null
+++ b/src/info_storages/file_info.rs
@@ -0,0 +1,57 @@
+use std::collections::HashMap;
+
+
+use serde::{Deserialize, Serialize};
+
+/// Information about file.
+/// It has everything about stored file.
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct FileInfo {
+    pub id: String,
+    pub offset: usize,
+    pub length: usize,
+    pub path: String,
+    pub created_at: i64,
+    pub deferred_size: bool,
+    pub metadata: HashMap<String, String>,
+}
+
+impl FileInfo {
+    /// Creates new `FileInfo`.
+    ///
+    /// # Params
+    ///
+    /// File info takes
+    /// `file_id` - Unique file identifier;
+    /// `file_size` - Size of a file if it's known;
+    /// `path` - local path of a file;
+    /// `initial_metadata` - meta information, that could be omitted.
+    pub fn new(
+        file_id: &str,
+        file_size: Option<usize>,
+        path: String,
+        initial_metadata: Option<HashMap<String, String>>,
+    ) -> FileInfo {
+        let id = String::from(file_id);
+        let mut length = 0;
+        let mut deferred_size = true;
+        if let Some(size) = file_size {
+            length = size;
+            deferred_size = false;
+        }
+        let metadata = match initial_metadata {
+            Some(meta) => meta,
+            None => HashMap::new(),
+        };
+
+        FileInfo {
+            id,
+            path,
+            length,
+            metadata,
+            deferred_size,
+            offset: 0,
+            created_at: chrono::Utc::now().timestamp(),
+        }
+    }
+}
diff --git a/src/info_storages/file_info_storage.rs b/src/info_storages/file_info_storage.rs
new file mode 100644
index 0000000..7341d6e
--- /dev/null
+++ b/src/info_storages/file_info_storage.rs
@@ -0,0 +1,87 @@
+use std::path::PathBuf;
+
+use async_std::fs::{DirBuilder, OpenOptions, read_to_string, remove_file};
+use async_std::prelude::*;
+use async_trait::async_trait;
+use log::error;
+
+use crate::errors::{RustusError, RustusResult};
+use crate::info_storages::{FileInfo, InfoStorage};
+use crate::RustusConf;
+
+pub struct FileInfoStorage {
+    app_conf: RustusConf,
+}
+
+impl FileInfoStorage {
+    pub fn new(app_conf: RustusConf) -> Self {
+        Self { app_conf }
+    }
+
+    pub fn info_file_path(&self, file_id: &str) -> PathBuf {
+        self.app_conf
+            .info_storage_opts
+            .info_dir
+            .join(format!("{}.info", file_id))
+    }
+}
+
+#[async_trait]
+impl InfoStorage for FileInfoStorage {
+    async fn prepare(&mut self) -> RustusResult<()> {
+        if !self.app_conf.info_storage_opts.info_dir.exists() {
+            DirBuilder::new()
+                .create(self.app_conf.info_storage_opts.info_dir.as_path())
+                .await
+                .map_err(|err| RustusError::UnableToPrepareInfoStorage(err.to_string()))?;
+        }
+        Ok(())
+    }
+
+    async fn set_info(&self, file_info: &FileInfo) -> RustusResult<()> {
+        let mut file = OpenOptions::new()
+            .write(true)
+            .create(true)
+            .open(self.info_file_path(file_info.id.as_str()).as_path())
+            .await
+            .map_err(|err| {
+                error!("{:?}", err);
+                RustusError::UnableToWrite(err.to_string())
+            })?;
+        file.write_all(serde_json::to_string(&file_info)?.as_bytes())
+            .await
+            .map_err(|err| {
+                error!("{:?}", err);
+                RustusError::UnableToWrite(
+                    self.info_file_path(file_info.id.as_str())
+                        .as_path()
+                        .display()
+                        .to_string(),
+                )
+            })?;
+        Ok(())
+    }
+
+    async fn get_info(&self, file_id: &str) -> RustusResult<FileInfo> {
+        let info_path = self.info_file_path(file_id);
+        if !info_path.exists() {
+            return Err(RustusError::FileNotFound);
+        }
+        let contents = read_to_string(info_path).await.map_err(|err| {
+            error!("{:?}", err);
+            RustusError::UnableToReadInfo
+        })?;
+        serde_json::from_str::<FileInfo>(contents.as_str()).map_err(RustusError::from)
+    }
+
+    async fn remove_info(&self, file_id: &str) -> RustusResult<()> {
+        let info_path = self.info_file_path(file_id);
+        if !info_path.exists() {
+            return Err(RustusError::FileNotFound);
+        }
+        remove_file(info_path).await.map_err(|err| {
+            error!("{:?}", err);
+            RustusError::UnableToRemove(String::from(file_id))
+        })
+    }
+}
diff --git a/src/info_storages/mod.rs b/src/info_storages/mod.rs
new file mode 100644
index 0000000..f88bce0
--- /dev/null
+++ b/src/info_storages/mod.rs
@@ -0,0 +1,53 @@
+use std::str::FromStr;
+
+use async_trait::async_trait;
+use derive_more::{Display, From};
+
+pub use file_info::FileInfo;
+
+use crate::errors::RustusResult;
+use crate::RustusConf;
+
+mod file_info;
+
+pub mod file_info_storage;
+
+
+#[derive(PartialEq, From, Display, Clone, Debug)]
+pub enum AvailableInfoStores {
+    #[display(fmt = "FileStorage")]
+    FileInfoStorage,
+}
+
+impl FromStr for AvailableInfoStores {
+    type Err = String;
+
+    fn from_str(input: &str) -> Result<Self, Self::Err> {
+        match input {
+            "file_info_storage" => Ok(AvailableInfoStores::FileInfoStorage),
+            _ => Err(String::from("Unknown storage type")),
+        }
+    }
+}
+
+impl AvailableInfoStores {
+    /// Convert `AvailableInfoStores` to the impl `InfoStorage`.
+    ///
+    /// # Params
+    /// `config` - Rustus configuration.
+    ///
+    pub fn get(&self, config: &RustusConf) -> Box<dyn InfoStorage + Sync + Send> {
+        #[allow(clippy::single_match)]
+        match self {
+            Self::FileInfoStorage => Box::new(file_info_storage::FileInfoStorage::new(config.clone())),
+        }
+    }
+}
+
+#[async_trait]
+pub trait InfoStorage {
+    async fn prepare(&mut self) -> RustusResult<()>;
+    async fn set_info(&self, file_info: &FileInfo) -> RustusResult<()>;
+    async fn get_info(&self, file_id: &str) -> RustusResult<FileInfo>;
+    async fn remove_info(&self, file_id: &str) -> RustusResult<()>;
+}
diff --git a/src/main.rs b/src/main.rs
index 5fe1730..4a71395 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,11 @@
 use std::str::FromStr;
 use std::sync::Arc;
 
-use actix_web::http::Method;
 use actix_web::{
-    dev::{Server, Service},
-    middleware, web, App, HttpServer,
+    App,
+    dev::{Server, Service}, HttpServer, middleware, web,
 };
+use actix_web::http::Method;
 use log::{error, info};
 
 use config::RustusConf;
@@ -14,6 +14,7 @@ use crate::storages::Storage;
 
 mod config;
 mod errors;
+mod info_storages;
 mod protocol;
 mod routes;
 mod storages;
@@ -82,7 +83,7 @@ pub fn create_server(
             // It returns 404 status_code.
             .default_service(web::route().to(routes::not_found))
     })
-    .bind((host, port))?;
+        .bind((host, port))?;
 
     // If custom workers count variable is provided.
     if let Some(workers_count) = workers {
@@ -98,7 +99,9 @@ async fn main() -> std::io::Result<()> {
     let app_conf = RustusConf::from_args();
     simple_logging::log_to_stderr(app_conf.log_level);
 
-    let mut storage = app_conf.storage_opts.storage.get(&app_conf);
+    let mut info_storage = app_conf.info_storage_opts.info_storage.get(&app_conf);
+    info_storage.prepare().await?;
+    let mut storage = app_conf.storage_opts.storage.get(&app_conf, info_storage);
     if let Err(err) = storage.prepare().await {
         error!("{}", err);
         return Err(err.into());
diff --git a/src/storages/file_storage.rs b/src/storages/file_storage.rs
index a9caf16..75b2628 100644
--- a/src/storages/file_storage.rs
+++ b/src/storages/file_storage.rs
@@ -2,44 +2,44 @@ use std::collections::HashMap;
 use std::path::PathBuf;
 
 use actix_files::NamedFile;
-use async_std::fs::{read_to_string, remove_file, DirBuilder, OpenOptions};
+use async_std::fs::{DirBuilder, OpenOptions, remove_file};
 use async_std::prelude::*;
 use async_trait::async_trait;
 use log::error;
 use uuid::Uuid;
 
 use crate::errors::{RustusError, RustusResult};
-use crate::storages::{FileInfo, Storage};
+use crate::info_storages::{FileInfo, InfoStorage};
 use crate::RustusConf;
+use crate::storages::Storage;
 
-#[derive(Clone)]
 pub struct FileStorage {
     app_conf: RustusConf,
+    info_storage: Box<dyn InfoStorage + Send + Sync>,
 }
 
 impl FileStorage {
-    pub fn new(app_conf: RustusConf) -> FileStorage {
-        FileStorage { app_conf }
+    pub fn new(app_conf: RustusConf, info_storage: Box<dyn InfoStorage + Send + Sync>) -> FileStorage {
+        FileStorage {
+            app_conf,
+            info_storage,
+        }
     }
 
-    pub fn info_file_path(&self, file_id: &str) -> PathBuf {
+    pub fn data_file_path(&self, file_id: &str) -> PathBuf {
         self.app_conf
             .storage_opts
-            .data
-            .join(format!("{}.info", file_id))
-    }
-
-    pub fn data_file_path(&self, file_id: &str) -> PathBuf {
-        self.app_conf.storage_opts.data.join(file_id.to_string())
+            .data_dir
+            .join(file_id.to_string())
     }
 }
 
 #[async_trait]
 impl Storage for FileStorage {
     async fn prepare(&mut self) -> RustusResult<()> {
-        if !self.app_conf.storage_opts.data.exists() {
+        if !self.app_conf.storage_opts.data_dir.exists() {
             DirBuilder::new()
-                .create(self.app_conf.storage_opts.data.as_path())
+                .create(self.app_conf.storage_opts.data_dir.as_path())
                 .await
                 .map_err(|err| RustusError::UnableToPrepareStorage(err.to_string()))?;
         }
@@ -47,39 +47,7 @@ impl Storage for FileStorage {
     }
 
     async fn get_file_info(&self, file_id: &str) -> RustusResult<FileInfo> {
-        let info_path = self.info_file_path(file_id);
-        if !info_path.exists() {
-            return Err(RustusError::FileNotFound);
-        }
-        let contents = read_to_string(info_path).await.map_err(|err| {
-            error!("{:?}", err);
-            RustusError::UnableToReadInfo
-        })?;
-        serde_json::from_str::<FileInfo>(contents.as_str()).map_err(RustusError::from)
-    }
-
-    async fn set_file_info(&self, file_info: &FileInfo) -> RustusResult<()> {
-        let mut file = OpenOptions::new()
-            .write(true)
-            .create(true)
-            .open(self.info_file_path(file_info.id.as_str()).as_path())
-            .await
-            .map_err(|err| {
-                error!("{:?}", err);
-                RustusError::UnableToWrite(err.to_string())
-            })?;
-        file.write_all(serde_json::to_string(&file_info)?.as_bytes())
-            .await
-            .map_err(|err| {
-                error!("{:?}", err);
-                RustusError::UnableToWrite(
-                    self.info_file_path(file_info.id.as_str())
-                        .as_path()
-                        .display()
-                        .to_string(),
-                )
-            })?;
-        Ok(())
+        self.info_storage.get_info(file_id).await
     }
 
     async fn get_contents(&self, file_id: &str) -> RustusResult<NamedFile> {
@@ -95,7 +63,7 @@ impl Storage for FileStorage {
         request_offset: usize,
         bytes: &[u8],
     ) -> RustusResult<usize> {
-        let mut info = self.get_file_info(file_id).await?;
+        let mut info = self.info_storage.get_info(file_id).await?;
         if info.offset != request_offset {
             return Err(RustusError::WrongOffset);
         }
@@ -114,7 +82,7 @@ impl Storage for FileStorage {
             RustusError::UnableToWrite(self.data_file_path(file_id).as_path().display().to_string())
         })?;
         info.offset += bytes.len();
-        self.set_file_info(&info).await?;
+        self.info_storage.set_info(&info).await?;
         Ok(info.offset)
     }
 
@@ -157,24 +125,17 @@ impl Storage for FileStorage {
             metadata,
         );
 
-        self.set_file_info(&file_info).await?;
+        self.info_storage.set_info(&file_info).await?;
 
         Ok(file_id)
     }
 
     async fn remove_file(&self, file_id: &str) -> RustusResult<()> {
-        let info_path = self.info_file_path(file_id);
-        if !info_path.exists() {
-            return Err(RustusError::FileNotFound);
-        }
+        self.info_storage.remove_info(file_id).await?;
         let data_path = self.data_file_path(file_id);
         if !data_path.exists() {
             return Err(RustusError::FileNotFound);
         }
-        remove_file(info_path).await.map_err(|err| {
-            error!("{:?}", err);
-            RustusError::UnableToRemove(String::from(file_id))
-        })?;
         remove_file(data_path).await.map_err(|err| {
             error!("{:?}", err);
             RustusError::UnableToRemove(String::from(file_id))
diff --git a/src/storages/mod.rs b/src/storages/mod.rs
index 9b11b3c..2a5cac9 100644
--- a/src/storages/mod.rs
+++ b/src/storages/mod.rs
@@ -3,25 +3,22 @@ use std::str::FromStr;
 
 use actix_files::NamedFile;
 use async_trait::async_trait;
-use chrono::serde::ts_seconds;
-use chrono::{DateTime, Utc};
+
+
 use derive_more::{Display, From};
-use serde::{Deserialize, Serialize};
-use sqlx::FromRow;
+
 
 use crate::errors::RustusResult;
+use crate::info_storages::{FileInfo, InfoStorage};
 use crate::RustusConf;
 
 pub mod file_storage;
-pub mod sqlite_file_storage;
 
 /// Enum of available Storage implementations.
 #[derive(PartialEq, From, Display, Clone, Debug)]
 pub enum AvailableStores {
     #[display(fmt = "FileStorage")]
     FileStorage,
-    #[display(fmt = "SqliteFileStorage")]
-    SqliteFileStorage,
 }
 
 impl FromStr for AvailableStores {
@@ -35,7 +32,6 @@ impl FromStr for AvailableStores {
     fn from_str(input: &str) -> Result<AvailableStores, Self::Err> {
         match input {
             "file_storage" => Ok(AvailableStores::FileStorage),
-            "sqlite_file_storage" => Ok(AvailableStores::SqliteFileStorage),
             _ => Err(String::from("Unknown storage type")),
         }
     }
@@ -47,66 +43,14 @@ impl AvailableStores {
     /// # Params
     /// `config` - Rustus configuration.
     ///
-    pub fn get(&self, config: &RustusConf) -> Box<dyn Storage + Send + Sync> {
+    pub fn get(
+        &self,
+        config: &RustusConf,
+        info_storage: Box<dyn InfoStorage + Sync + Send>,
+    ) -> Box<dyn Storage + Send + Sync> {
+        #[allow(clippy::single_match)]
         match self {
-            Self::FileStorage => Box::new(file_storage::FileStorage::new(config.clone())),
-            Self::SqliteFileStorage => {
-                Box::new(sqlite_file_storage::SQLiteFileStorage::new(config.clone()))
-            }
-        }
-    }
-}
-
-/// Information about file.
-/// It has everything about stored file.
-#[derive(Clone, Debug, Serialize, Deserialize, FromRow)]
-pub struct FileInfo {
-    pub id: String,
-    pub offset: usize,
-    pub length: usize,
-    pub path: String,
-    #[serde(with = "ts_seconds")]
-    pub created_at: DateTime<Utc>,
-    pub deferred_size: bool,
-    pub metadata: HashMap<String, String>,
-}
-
-impl FileInfo {
-    /// Creates new `FileInfo`.
-    ///
-    /// # Params
-    ///
-    /// File info takes
-    /// `file_id` - Unique file identifier;
-    /// `file_size` - Size of a file if it's known;
-    /// `path` - local path of a file;
-    /// `initial_metadata` - meta information, that could be omitted.
-    pub fn new(
-        file_id: &str,
-        file_size: Option<usize>,
-        path: String,
-        initial_metadata: Option<HashMap<String, String>>,
-    ) -> FileInfo {
-        let id = String::from(file_id);
-        let mut length = 0;
-        let mut deferred_size = true;
-        if let Some(size) = file_size {
-            length = size;
-            deferred_size = false;
-        }
-        let metadata = match initial_metadata {
-            Some(meta) => meta,
-            None => HashMap::new(),
-        };
-
-        FileInfo {
-            id,
-            path,
-            length,
-            metadata,
-            deferred_size,
-            offset: 0,
-            created_at: chrono::Utc::now(),
+            Self::FileStorage => Box::new(file_storage::FileStorage::new(config.clone(), info_storage)),
         }
     }
 }
@@ -128,14 +72,6 @@ pub trait Storage {
     /// `file_id` - unique file identifier.
     async fn get_file_info(&self, file_id: &str) -> RustusResult<FileInfo>;
 
-    /// Set file info
-    ///
-    /// This method will save information about current upload.
-    ///
-    /// # Params
-    /// `file_info` - information about current upload.
-    async fn set_file_info(&self, file_info: &FileInfo) -> RustusResult<()>;
-
     /// Get contents of a file.
     ///
     /// This method must return NamedFile since it
diff --git a/src/storages/sqlite_file_storage.rs b/src/storages/sqlite_file_storage.rs
deleted file mode 100644
index 94ee774..0000000
--- a/src/storages/sqlite_file_storage.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use std::collections::HashMap;
-
-use actix_files::NamedFile;
-use async_std::fs::{DirBuilder, File};
-use async_trait::async_trait;
-use log::error;
-use sqlx::sqlite::SqlitePoolOptions;
-use sqlx::SqlitePool;
-use thiserror::private::PathAsDisplay;
-
-use crate::errors::{RustusError, RustusResult};
-use crate::storages::{FileInfo, Storage};
-use crate::RustusConf;
-
-#[derive(Clone)]
-pub struct SQLiteFileStorage {
-    app_conf: RustusConf,
-    pool: Option<SqlitePool>,
-}
-
-impl SQLiteFileStorage {
-    pub fn new(app_conf: RustusConf) -> SQLiteFileStorage {
-        SQLiteFileStorage {
-            app_conf,
-            pool: None,
-        }
-    }
-
-    #[allow(dead_code)]
-    pub fn get_pool(&self) -> RustusResult<&SqlitePool> {
-        if let Some(pool) = &self.pool {
-            Ok(pool)
-        } else {
-            error!("Pool doesn't exist.");
-            Err(RustusError::Unknown)
-        }
-    }
-}
-
-#[async_trait]
-impl Storage for SQLiteFileStorage {
-    async fn prepare(&mut self) -> RustusResult<()> {
-        if !self.app_conf.storage_opts.data.exists() {
-            DirBuilder::new()
-                .create(self.app_conf.storage_opts.data.as_path())
-                .await
-                .map_err(|err| RustusError::UnableToPrepareStorage(err.to_string()))?;
-        }
-        if !self.app_conf.storage_opts.sqlite_dsn.exists() {
-            File::create(self.app_conf.storage_opts.sqlite_dsn.clone())
-                .await
-                .map_err(|err| RustusError::UnableToPrepareStorage(err.to_string()))?;
-        }
-        let pool = SqlitePoolOptions::new()
-            .max_connections(10)
-            .connect(
-                format!(
-                    "sqlite://{}",
-                    self.app_conf
-                        .storage_opts
-                        .sqlite_dsn
-                        .as_display()
-                        .to_string()
-                )
-                .as_str(),
-            )
-            .await
-            .map_err(RustusError::from)?;
-        sqlx::query(
-            "CREATE TABLE IF NOT EXISTS \
-                fileinfo(\
-                    id VARCHAR(40) PRIMARY KEY, \
-                    offset UNSIGNED BIG INT NOT NULL DEFAULT 0, \
-                    length UNSIGNED BIG INT, \
-                    path TEXT, \
-                    created_at DATETIME, \
-                    deferred_size BOOLEAN, \
-                    metadata TEXT\
-               );",
-        )
-        .execute(&pool)
-        .await?;
-        self.pool = Some(pool);
-        Ok(())
-    }
-
-    async fn get_file_info(&self, _file_id: &str) -> RustusResult<FileInfo> {
-        todo!()
-    }
-
-    async fn set_file_info(&self, _file_info: &FileInfo) -> RustusResult<()> {
-        todo!()
-    }
-
-    async fn get_contents(&self, _file_id: &str) -> RustusResult<NamedFile> {
-        todo!()
-    }
-
-    async fn add_bytes(
-        &self,
-        _file_id: &str,
-        _request_offset: usize,
-        _bytes: &[u8],
-    ) -> RustusResult<usize> {
-        todo!()
-    }
-
-    async fn create_file(
-        &self,
-        _file_size: Option<usize>,
-        _metadata: Option<HashMap<String, String>>,
-    ) -> RustusResult<String> {
-        todo!()
-    }
-
-    async fn remove_file(&self, _file_id: &str) -> RustusResult<()> {
-        todo!()
-    }
-}
-- 
GitLab