mirror of
https://github.com/TeFiLeDo/listload.git
synced 2024-11-25 05:36:16 +01:00
add configuration
This commit is contained in:
parent
833e41fb43
commit
4ee286e7c6
@ -7,7 +7,8 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
clap = { version = "4.3.8", features = ["derive", "env"] }
|
clap = { version = "4.3.8", features = ["cargo", "derive", "env", "wrap_help"] }
|
||||||
|
directories = "5.0.1"
|
||||||
downloader = { version = "0.2.7", default-features = false, features = ["rustls-tls"] }
|
downloader = { version = "0.2.7", default-features = false, features = ["rustls-tls"] }
|
||||||
serde = { version = "1.0.164", features = ["derive"] }
|
serde = { version = "1.0.164", features = ["derive"] }
|
||||||
serde_json = "1.0.99"
|
serde_json = "1.0.99"
|
||||||
|
107
src/config.rs
Normal file
107
src/config.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
fs::File,
|
||||||
|
io::{BufReader, Read},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
|
use downloader::Downloader;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{PROJ_DIRS, USER_DIRS};
|
||||||
|
|
||||||
|
/// Global configuration values.
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields, default)]
|
||||||
|
pub struct Config {
|
||||||
|
/// The base directory used for resolving relative paths. Defaults to the home directory.
|
||||||
|
pub base_directory: PathBuf,
|
||||||
|
/// The number of parallel downloads. Defaults to 32.
|
||||||
|
pub parallel_downloads: u16,
|
||||||
|
/// The number of retries. Defaults to 3.
|
||||||
|
pub retries: u16,
|
||||||
|
/// The timeout for establishing a connection in seconds. Defaults to 15.
|
||||||
|
pub timeout_connection: u64,
|
||||||
|
/// The timeout for downloading a file in seconds. Defaults to 30.
|
||||||
|
pub timeout_download: u64,
|
||||||
|
/// The user agent to use for HTTP communication. Default is not stabilized.
|
||||||
|
pub user_agent: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
/// Read the configuration from the default file, or revert to default values if unavailable.
|
||||||
|
pub fn read_from_default_file() -> anyhow::Result<Self> {
|
||||||
|
let dirs = PROJ_DIRS.get().expect("directories not initialized");
|
||||||
|
|
||||||
|
let mut path = dirs.config_dir().to_path_buf();
|
||||||
|
path.push("config.toml");
|
||||||
|
|
||||||
|
if path.is_file() {
|
||||||
|
Self::read_from_file(&path)
|
||||||
|
} else if !path.exists() {
|
||||||
|
Ok(Self::default())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"default configuration file is neither file nor nonexistent"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the configuration from the specified file.
|
||||||
|
pub fn read_from_file(path: &Path) -> anyhow::Result<Self> {
|
||||||
|
let file = File::open(path).context("failed to open config file")?;
|
||||||
|
let mut buf = BufReader::new(file);
|
||||||
|
|
||||||
|
let mut data = String::new();
|
||||||
|
buf.read_to_string(&mut data)
|
||||||
|
.context("failed to read config data")?;
|
||||||
|
|
||||||
|
toml::from_str(&data).context("failed to parse config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`Downloader`] with the configuration applied.
|
||||||
|
pub fn downloader(&self) -> anyhow::Result<Downloader> {
|
||||||
|
Downloader::builder()
|
||||||
|
.user_agent(&self.user_agent)
|
||||||
|
.connect_timeout(Duration::from_secs(self.timeout_connection))
|
||||||
|
.timeout(Duration::from_secs(self.timeout_download))
|
||||||
|
.parallel_requests(self.parallel_downloads)
|
||||||
|
.retries(self.retries)
|
||||||
|
.download_folder(&self.base_directory)
|
||||||
|
.build()
|
||||||
|
.context("failed to build downloader")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
let dirs = USER_DIRS.get().expect("directories not initialized");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
base_directory: dirs.home_dir().to_path_buf(),
|
||||||
|
parallel_downloads: 32,
|
||||||
|
retries: 3,
|
||||||
|
timeout_connection: 15,
|
||||||
|
timeout_download: 30,
|
||||||
|
user_agent: format!(
|
||||||
|
"{} {}.{}",
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
env!("CARGO_PKG_VERSION_MAJOR"),
|
||||||
|
env!("CARGO_PKG_VERSION_MINOR")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Config {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
writeln!(f, "base directory: {}", self.base_directory.display())?;
|
||||||
|
writeln!(f, "parallel downloads: {}", self.parallel_downloads)?;
|
||||||
|
writeln!(f, "retries: {}", self.retries)?;
|
||||||
|
writeln!(f, "timeout connection: {}s", self.timeout_connection)?;
|
||||||
|
writeln!(f, "timeout download: {}s", self.timeout_download)?;
|
||||||
|
write!(f, "user agent: {}", self.user_agent)
|
||||||
|
}
|
||||||
|
}
|
54
src/main.rs
54
src/main.rs
@ -1,3 +1,53 @@
|
|||||||
fn main() {
|
use std::sync::OnceLock;
|
||||||
println!("Hello, world!");
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use config::Config;
|
||||||
|
use directories::{ProjectDirs, UserDirs};
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
static USER_DIRS: OnceLock<UserDirs> = OnceLock::new();
|
||||||
|
static PROJ_DIRS: OnceLock<ProjectDirs> = OnceLock::new();
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let cli = CLI::parse();
|
||||||
|
|
||||||
|
// initialize dirs
|
||||||
|
let user_dirs = UserDirs::new().context("failed to discover user directiories")?;
|
||||||
|
let proj_dirs = ProjectDirs::from("dev", "TFLD", "ListLoad")
|
||||||
|
.context("failed to discover program directories")?;
|
||||||
|
|
||||||
|
USER_DIRS
|
||||||
|
.set(user_dirs)
|
||||||
|
.ok()
|
||||||
|
.context("failed to initialize user directories")?;
|
||||||
|
PROJ_DIRS
|
||||||
|
.set(proj_dirs)
|
||||||
|
.ok()
|
||||||
|
.context("failed to initialize program directories")?;
|
||||||
|
|
||||||
|
// initialize downloading
|
||||||
|
let cfg = Config::read_from_default_file().context("failed to load config")?;
|
||||||
|
let downloader = cfg.downloader().context("failed to create downloader")?;
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
CMD::Config => {
|
||||||
|
println!("{cfg}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[clap(about, author, version)]
|
||||||
|
struct CLI {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
command: CMD,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum CMD {
|
||||||
|
/// Print out the current configuration.
|
||||||
|
Config,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user