Refactored the whole project to have a proper config file system instead of using a .env file

Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
Louis Vallat 2021-10-27 11:44:15 +02:00
parent 636e77ab05
commit c84646671e
No known key found for this signature in database
GPG Key ID: 0C87282F76E61283
7 changed files with 119 additions and 72 deletions

View File

@ -1,4 +0,0 @@
LYCHEE_ENDPOINT=""
LYCHEE_API_KEY=""
LYCHEE_USERNAME=""
LYCHEE_PASSWORD=""

21
Cargo.lock generated
View File

@ -194,12 +194,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -455,13 +449,14 @@ dependencies = [
"clap", "clap",
"cookie", "cookie",
"dirs", "dirs",
"dotenv",
"hyper", "hyper",
"hyper-tls", "hyper-tls",
"json", "json",
"mockito", "mockito",
"serde",
"tokio", "tokio",
"tokio-test", "tokio-test",
"toml",
] ]
[[package]] [[package]]
@ -830,6 +825,9 @@ name = "serde"
version = "1.0.130" version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
@ -1117,6 +1115,15 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.1" version = "0.3.1"

View File

@ -10,11 +10,12 @@ hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
tokio-test = "0.4.2" tokio-test = "0.4.2"
hyper-tls = "0.5.0" hyper-tls = "0.5.0"
dotenv = "0.15.0"
json = "0.12.4" json = "0.12.4"
clap = "2.33.3" clap = "2.33.3"
dirs = "4.0.0" dirs = "4.0.0"
cookie = "0.15.1" cookie = "0.15.1"
toml = "0.5.8"
serde = { version = "1", features = ["derive"] }
[dev-dependencies] [dev-dependencies]
mockito = "0.30.0" mockito = "0.30.0"

57
src/config.rs Normal file
View File

@ -0,0 +1,57 @@
use std::{fs::{read_to_string, create_dir_all, File}, path::{PathBuf, Path}};
use serde::{Serialize, Deserialize};
use crate::utils::write_to_file;
#[derive(Deserialize, Serialize)]
pub struct Config {
pub api_key: String,
pub endpoint: String
}
pub fn save_config(c: &Config) {
let conf = toml::to_string(c).unwrap();
write_to_file(conf.as_str(), get_config_file().to_str().unwrap());
}
pub fn read_config() -> Config {
let c = read_to_string(get_config_file().to_str().unwrap().to_string()).unwrap();
let conf: Config = toml::from_str(c.as_str()).expect("Invalid configuration file.");
return conf;
}
pub fn setup_config_data_storage() {
create_dir_all(get_config_folder()).expect("Cannot create config base folder.");
let s = get_session_file();
if !Path::new(s.to_str().unwrap()).exists() {
File::create(s).expect("Cannot create session file.");
}
let c = get_config_file();
if !Path::new(c.to_str().unwrap()).exists() {
let conf = Config {
api_key: "YOUR API KEY HERE".to_string(),
endpoint: "YOUR ENDPOINT HERE".to_string()
};
save_config(&conf);
panic!("Warning, new configuration file created, consider editing '{}' before running", c.to_str().unwrap());
}
}
fn get_config_folder() -> PathBuf {
let mut p = dirs::config_dir().unwrap();
p.push("Lychee_CLIent");
return p;
}
pub fn get_config_file() -> PathBuf {
let mut p = get_config_folder();
p.push("config.toml");
return p;
}
pub fn get_session_file() -> PathBuf {
let mut p = get_config_folder();
p.push("session");
return p;
}

View File

@ -1,15 +1,14 @@
use std::{fs::{File, create_dir_all}, io::{Read, Write}, path::{Path, PathBuf}}; use crate::{albums::get_albums, config::{get_session_file, read_config, setup_config_data_storage}, session::login, utils::{write_to_file, read_from_file, body_to_str}};
use hyper_tls::HttpsConnector; use hyper_tls::HttpsConnector;
use hyper::{Body, Client, body, client::HttpConnector}; use hyper::{Client, client::HttpConnector};
use clap::{App, Arg, SubCommand, crate_version}; use clap::{App, Arg, SubCommand, crate_version};
use session::logout; use session::logout;
use cookie::Cookie; use cookie::Cookie;
use crate::{albums::get_albums, session::login};
use dotenv::dotenv;
mod session;
mod albums; mod albums;
mod session;
mod utils;
mod config;
pub struct LycheeClient { pub struct LycheeClient {
client: Client<HttpsConnector<HttpConnector>>, client: Client<HttpsConnector<HttpConnector>>,
@ -56,16 +55,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
); );
let matches = app.clone().get_matches(); let matches = app.clone().get_matches();
dotenv().ok(); let c = read_config();
let client = LycheeClient { let client = LycheeClient {
client: Client::builder().build::<_, hyper::Body>(HttpsConnector::new()), client: Client::builder().build::<_, hyper::Body>(HttpsConnector::new()),
endpoint: std::env::var("LYCHEE_ENDPOINT").unwrap(), endpoint: c.endpoint,
api_key: std::env::var("LYCHEE_API_KEY").unwrap() api_key: c.api_key
}; };
let mut session_path = get_config_folder(); let session_path = get_session_file();
session_path.push("session");
let lychee_session = Cookie::parse(read_from_file(session_path.to_str().unwrap())) let lychee_session = Cookie::parse(read_from_file(session_path.to_str().unwrap()))
.expect("Cannot parse cookie from session storage."); .expect("Cannot parse cookie from session storage.");
let lychee_session_cookie = lychee_session.name().to_string() + "=" + lychee_session.value(); let lychee_session_cookie = lychee_session.name().to_string() + "=" + lychee_session.value();
@ -89,51 +87,3 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Ok(()) Ok(())
} }
pub async fn body_to_str(res: Body) -> String {
return String::from_utf8(body::to_bytes(res).await.unwrap().to_vec()).unwrap();
}
fn setup_config_data_storage() {
create_dir_all(get_config_folder()).expect("Cannot create folder.");
let mut session = get_config_folder();
session.push("session");
if !Path::new(session.to_str().expect("Cannot check path existence.")).exists() {
File::create(session).expect("Cannot create session file.");
}
}
fn get_config_folder() -> PathBuf {
let mut p = dirs::config_dir().unwrap();
p.push("Lychee_CLIent");
return p;
}
fn write_to_file(d: &str, p: &str) {
let mut o = File::create(p).expect("Cannot create file.");
o.write_all(d.as_bytes()).expect("Cannot write to file.");
}
fn read_from_file(p: &str) -> String {
if !Path::new(p).exists() { return "".to_string(); }
let mut d = String::new();
let mut ifile = File::open(p).expect("Cannot open file.");
ifile.read_to_string(&mut d).expect("Cannot read file.");
return d;
}
#[cfg(test)]
mod tests_main {
use hyper::Body;
use crate::body_to_str;
#[test]
fn body_to_str_empty() {
assert_eq!("", tokio_test::block_on(body_to_str(Body::empty())));
}
#[test]
fn body_to_str_not_empty() {
assert_eq!("demo", tokio_test::block_on(body_to_str(Body::from("demo"))));
assert_eq!("hello world", tokio_test::block_on(body_to_str(Body::from("hello world"))));
}
}

View File

@ -32,7 +32,7 @@ pub async fn logout(client: &LycheeClient, lychee_session: &str) {
.expect("Cannot request Session::logout."); .expect("Cannot request Session::logout.");
let res = client.client.request(req).await.unwrap(); let res = client.client.request(req).await.unwrap();
let body = body_to_str(res.into_body()).await; let body = body_to_str(res.into_body()).await;
if body != "true" { panic!("Error while logging in, expected true, server returned: {}", body); } if body != "true" { panic!("Error while logging out, expected true, server returned: {}", body); }
} }
#[cfg(test)] #[cfg(test)]

36
src/utils.rs Normal file
View File

@ -0,0 +1,36 @@
use std::{fs::File, io::{Read, Write}, path::Path};
use hyper::{Body, body};
pub async fn body_to_str(res: Body) -> String {
return String::from_utf8(body::to_bytes(res).await.unwrap().to_vec()).unwrap();
}
pub fn write_to_file(d: &str, p: &str) {
let mut o = File::create(p).expect("Cannot create file.");
o.write_all(d.as_bytes()).expect("Cannot write to file.");
}
pub fn read_from_file(p: &str) -> String {
if !Path::new(p).exists() { return "".to_string(); }
let mut d = String::new();
let mut ifile = File::open(p).expect("Cannot open file.");
ifile.read_to_string(&mut d).expect("Cannot read file.");
return d;
}
#[cfg(test)]
mod utils_tests {
use hyper::Body;
use crate::utils::body_to_str;
#[test]
fn body_to_str_empty() {
assert_eq!("", tokio_test::block_on(body_to_str(Body::empty())));
}
#[test]
fn body_to_str_not_empty() {
assert_eq!("demo", tokio_test::block_on(body_to_str(Body::from("demo"))));
assert_eq!("hello world", tokio_test::block_on(body_to_str(Body::from("hello world"))));
}
}