feat: load settings from configuration file

This commit is contained in:
Aravinth Manivannan 2022-04-02 16:29:23 +05:30
parent 68704573c6
commit e37f335652
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
4 changed files with 545 additions and 89 deletions

266
Cargo.lock generated
View file

@ -29,12 +29,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -59,6 +53,12 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-compression"
version = "0.3.12"
@ -197,7 +197,7 @@ checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"num-traits 0.2.14",
"winapi",
]
@ -225,23 +225,26 @@ dependencies = [
[[package]]
name = "config"
version = "0.12.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54ad70579325f1a38ea4c13412b82241c5900700a69785d73e2736bd65a33f86"
checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369"
dependencies = [
"async-trait",
"json5",
"lazy_static",
"nom",
"pathdiff",
"ron",
"rust-ini",
"serde",
"serde 1.0.136",
"serde-hjson",
"serde_json",
"toml",
"yaml-rust",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -283,6 +286,19 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "deunicode"
version = "0.4.3"
@ -298,15 +314,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "dlv-list"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b"
dependencies = [
"rand",
]
[[package]]
name = "encoding_rs"
version = "0.8.30"
@ -495,15 +502,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -637,6 +635,12 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]]
name = "ignore"
version = "0.4.18"
@ -662,7 +666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown 0.11.2",
"hashbrown",
]
[[package]]
@ -707,23 +711,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexical-core"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.121"
@ -793,12 +799,6 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
@ -852,12 +852,13 @@ dependencies = [
[[package]]
name = "nom"
version = "7.1.1"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"lexical-core",
"memchr",
"minimal-lexical",
"version_check",
]
[[package]]
@ -876,7 +877,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
"num-traits 0.2.14",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.14",
]
[[package]]
@ -943,16 +953,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "ordered-multimap"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485"
dependencies = [
"dlv-list",
"hashbrown 0.9.1",
]
[[package]]
name = "parking_lot"
version = "0.12.0"
@ -985,12 +985,6 @@ dependencies = [
"regex",
]
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1103,6 +1097,30 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
@ -1221,7 +1239,7 @@ dependencies = [
"rustls",
"rustls-native-certs",
"rustls-pemfile 0.3.0",
"serde",
"serde 1.0.136",
"serde_json",
"serde_urlencoded",
"tokio",
@ -1261,24 +1279,18 @@ dependencies = [
]
[[package]]
name = "ron"
version = "0.7.0"
name = "rust-ini"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678"
dependencies = [
"base64",
"bitflags",
"serde",
]
checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
[[package]]
name = "rust-ini"
version = "0.17.0"
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"cfg-if",
"ordered-multimap",
"semver",
]
[[package]]
@ -1387,6 +1399,18 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
[[package]]
name = "serde"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
[[package]]
name = "serde"
version = "1.0.136"
@ -1396,6 +1420,18 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-hjson"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8"
dependencies = [
"lazy_static",
"num-traits 0.1.43",
"regex",
"serde 0.8.23",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
@ -1415,7 +1451,7 @@ checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
dependencies = [
"itoa",
"ryu",
"serde",
"serde 1.0.136",
]
[[package]]
@ -1427,7 +1463,7 @@ dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
"serde 1.0.136",
]
[[package]]
@ -1500,17 +1536,26 @@ version = "0.1.0"
dependencies = [
"actix-rt",
"config",
"derive_more",
"lazy_static",
"log",
"rand",
"reqwest",
"serde",
"serde 1.0.136",
"serde_json",
"tera",
"tokio",
"trust-dns-resolver",
"url",
"validator",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.89"
@ -1552,7 +1597,7 @@ dependencies = [
"pest_derive",
"rand",
"regex",
"serde",
"serde 1.0.136",
"serde_json",
"slug",
"unic-segment",
@ -1662,7 +1707,7 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
"serde 1.0.136",
]
[[package]]
@ -1859,6 +1904,49 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "validator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0f08911ab0fee2c5009580f04615fa868898ee57de10692a45da0c3bcc3e5e"
dependencies = [
"idna",
"lazy_static",
"regex",
"serde 1.0.136",
"serde_derive",
"serde_json",
"url",
"validator_derive",
"validator_types",
]
[[package]]
name = "validator_derive"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d85135714dba11a1bd0b3eb1744169266f1a38977bf4e3ff5e2e1acb8c2b7eee"
dependencies = [
"if_chain",
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
"regex",
"syn",
"validator_types",
]
[[package]]
name = "validator_types"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded9d97e1d42327632f5f3bae6403c04886e2de3036261ef42deebd931a6a291"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "vcpkg"
version = "0.2.15"

36
config/default.toml Normal file
View file

@ -0,0 +1,36 @@
log = "info" # possible values: "info", "warn", "trace", "error", "debug"
source_code = "https://github.com/forgeflux-org/starchart"
allow_new_index = true # allow registration on server
admin_email = "admin@starchart.example.com"
[server]
# The port at which you want authentication to listen to
# takes a number, choose from 1000-10000 if you dont know what you are doing
port = 7000
#IP address. Enter 0.0.0.0 to listen on all availale addresses
ip= "0.0.0.0"
# enter your hostname, eg: example.com
domain = "localhost"
proxy_has_tls = false
cookie_secret = "f12d9adf4e364648664442b8f50bf478e748e1d77c4797b2ec1f56803278"
#workers = 2
[database]
# This section deals with the database location and how to access it
# Please note that at the moment, we have support for only postgresqa.
# Example, if you are Batman, your config would be:
# hostname = "batcave.org"
# port = "5432"
# username = "batman"
# password = "somereallycomplicatedBatmanpassword"
hostname = "localhost"
port = "5432"
username = "postgres"
password = "password"
name = "postgres"
pool = 4
database_type = "postgres"
[repository]
root = "/tmp/starchart.batsense.net"

View file

@ -16,11 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
pub mod gitea;
pub mod settings;
pub mod utils;
pub mod verify;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");
pub const GIT_COMMIT_HASH: &str = env!("GIT_HASH");
pub const DOMAIN: &str = "developer-starchart.forgeflux.org";
#[actix_rt::main]

330
src/settings.rs Normal file
View file

@ -0,0 +1,330 @@
/*
* ForgeFlux StarChart - A federated software forge spider
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::path::Path;
use std::{env, fs};
use config::{Config, ConfigError, Environment, File};
use derive_more::Display;
use log::warn;
use serde::Deserialize;
use url::Url;
use validator::Validate;
#[derive(Debug, Clone, Deserialize)]
pub struct Server {
pub port: u32,
pub domain: String,
pub ip: String,
pub proxy_has_tls: bool,
}
impl Server {
#[cfg(not(tarpaulin_include))]
pub fn get_ip(&self) -> String {
format!("{}:{}", self.ip, self.port)
}
}
#[derive(Deserialize, Display, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum LogLevel {
#[display(fmt = "debug")]
Debug,
#[display(fmt = "info")]
Info,
#[display(fmt = "trace")]
Trace,
#[display(fmt = "error")]
Error,
#[display(fmt = "warn")]
Warn,
}
impl LogLevel {
fn set_log_level(&self) {
const LOG_VAR: &str = "RUST_LOG";
if env::var(LOG_VAR).is_err() {
env::set_var("RUST_LOG", format!("{}", self));
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Repository {
pub root: String,
}
impl Repository {
fn create_root_dir(&self) {
let root = Path::new(&self.root);
if root.exists() {
if !root.is_dir() {
fs::remove_file(&root).unwrap();
fs::create_dir_all(&root).unwrap();
}
} else {
fs::create_dir_all(&root).unwrap();
}
}
}
#[derive(Deserialize, Display, PartialEq, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum DBType {
#[display(fmt = "postgres")]
Postgres,
#[display(fmt = "sqlite")]
Sqlite,
}
impl DBType {
fn from_url(url: &Url) -> Result<Self, ConfigError> {
match url.scheme() {
"sqlite" => Ok(Self::Sqlite),
"postgres" => Ok(Self::Postgres),
_ => Err(ConfigError::Message("Unknown database type".into())),
}
}
}
#[derive(Debug, Clone, Deserialize)]
struct DatabaseBuilder {
pub port: u32,
pub hostname: String,
pub username: String,
pub password: String,
pub name: String,
pub database_type: DBType,
}
impl DatabaseBuilder {
#[cfg(not(tarpaulin_include))]
fn extract_database_url(url: &Url) -> Self {
log::debug!("Databse name: {}", url.path());
let mut path = url.path().split('/');
path.next();
let name = path.next().expect("no database name").to_string();
DatabaseBuilder {
port: url.port().expect("Enter database port").into(),
hostname: url.host().expect("Enter database host").to_string(),
username: url.username().into(),
password: url.password().expect("Enter database password").into(),
name,
database_type: DBType::from_url(url).unwrap(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Database {
pub url: String,
pub pool: u32,
pub database_type: DBType,
}
#[derive(Debug, Validate, Clone, Deserialize)]
pub struct Settings {
pub log: LogLevel,
pub database: Database,
pub allow_new_index: bool,
pub server: Server,
#[validate(url)]
pub source_code: String,
pub repository: Repository,
#[validate(email)]
pub admin_email: String,
}
#[cfg(not(tarpaulin_include))]
impl Settings {
fn set_source_code(&mut self) {
if !self.source_code.ends_with('/') {
self.source_code.push('/');
}
let mut base = url::Url::parse(&self.source_code).unwrap();
base = base.join("tree/").unwrap();
base = base.join(crate::GIT_COMMIT_HASH).unwrap();
self.source_code = base.into();
}
pub fn new() -> Result<Self, ConfigError> {
let mut s = Config::new();
// setting default values
#[cfg(test)]
s.set_default("database.pool", 2.to_string())
.expect("Couldn't get the number of CPUs");
const CURRENT_DIR: &str = "./config/default.toml";
const ETC: &str = "/etc/starchart/config.toml";
if let Ok(path) = env::var("STARCHART_CONFIG") {
s.merge(File::with_name(&path))?;
} else if Path::new(CURRENT_DIR).exists() {
// merging default config from file
s.merge(File::with_name(CURRENT_DIR))?;
} else if Path::new(ETC).exists() {
s.merge(File::with_name(ETC))?;
} else {
log::warn!("configuration file not found");
}
s.merge(Environment::with_prefix("STARCHART").separator("__"))?;
check_url(&s);
match env::var("PORT") {
Ok(val) => {
s.set("server.port", val).unwrap();
}
Err(e) => warn!("couldn't interpret PORT: {}", e),
}
match env::var("DATABASE_URL") {
Ok(val) => {
let url = Url::parse(&val).expect("couldn't parse Database URL");
let database_conf = DatabaseBuilder::extract_database_url(&url);
set_from_database_url(&mut s, &database_conf);
}
Err(e) => warn!("couldn't interpret DATABASE_URL: {}", e),
}
set_database_url(&mut s);
let mut settings: Settings = s.try_into()?;
settings.log.set_log_level();
settings.repository.create_root_dir();
settings.validate().unwrap();
settings.set_source_code();
settings.validate().unwrap();
Ok(settings)
}
}
#[cfg(not(tarpaulin_include))]
fn check_url(s: &Config) {
let url = s
.get::<String>("source_code")
.expect("Couldn't access source_code");
Url::parse(&url).expect("Please enter a URL for source_code in settings");
}
#[cfg(not(tarpaulin_include))]
fn set_from_database_url(s: &mut Config, database_conf: &DatabaseBuilder) {
s.set("database.username", database_conf.username.clone())
.expect("Couldn't set database username");
s.set("database.password", database_conf.password.clone())
.expect("Couldn't access database password");
s.set("database.hostname", database_conf.hostname.clone())
.expect("Couldn't access database hostname");
s.set("database.port", database_conf.port as i64)
.expect("Couldn't access database port");
s.set("database.name", database_conf.name.clone())
.expect("Couldn't access database name");
s.set(
"database.database_type",
format!("{}", database_conf.database_type),
)
.expect("Couldn't access database type");
}
#[cfg(not(tarpaulin_include))]
fn set_database_url(s: &mut Config) {
s.set(
"database.url",
format!(
r"{}://{}:{}@{}:{}/{}",
s.get::<String>("database.database_type")
.expect("Couldn't access database database_type"),
s.get::<String>("database.username")
.expect("Couldn't access database username"),
s.get::<String>("database.password")
.expect("Couldn't access database password"),
s.get::<String>("database.hostname")
.expect("Couldn't access database hostname"),
s.get::<String>("database.port")
.expect("Couldn't access database port"),
s.get::<String>("database.name")
.expect("Couldn't access database name")
),
)
.expect("Couldn't set databse url");
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::get_random;
#[test]
fn database_type_test() {
for i in ["sqlite://foo", "postgres://bar", "unknown://"].iter() {
let url = Url::parse(i).unwrap();
if i.contains("sqlite") {
assert_eq!(DBType::from_url(&url).unwrap(), DBType::Sqlite);
} else if i.contains("unknown") {
assert!(DBType::from_url(&url).is_err());
} else {
assert_eq!(DBType::from_url(&url).unwrap(), DBType::Postgres);
}
}
}
#[test]
fn root_dir_is_created_test() {
let dir;
loop {
let mut tmp = env::temp_dir();
tmp = tmp.join(get_random(10));
if tmp.exists() {
continue;
} else {
dir = tmp;
break;
}
}
let repo = Repository {
root: dir.to_str().unwrap().to_owned(),
};
repo.create_root_dir();
assert!(dir.exists());
assert!(dir.is_dir());
let file = dir.join("foo");
fs::write(&file, "foo").unwrap();
repo.create_root_dir();
assert!(dir.exists());
assert!(dir.is_dir());
assert!(file.exists());
assert!(file.is_file());
let repo = Repository {
root: file.to_str().unwrap().to_owned(),
};
repo.create_root_dir();
assert!(file.exists());
assert!(file.is_dir());
}
}