src files
This commit is contained in:
198
src/common.rs
Normal file
198
src/common.rs
Normal file
@@ -0,0 +1,198 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_stream::stream;
|
||||
use clap::Parser;
|
||||
use encoding::{label::encoding_from_whatwg_label, EncoderTrap};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
};
|
||||
|
||||
use futures_core::stream::Stream;
|
||||
|
||||
pub(crate) type VarsMap = BTreeMap<String, String>;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub(crate) struct Args {
|
||||
/// new client name
|
||||
pub(crate) name: String,
|
||||
|
||||
/// pki directory
|
||||
#[arg(short, long)]
|
||||
pub(crate) directory: Option<String>,
|
||||
|
||||
/// client email
|
||||
#[arg(short, long)]
|
||||
pub(crate) email: Option<String>,
|
||||
|
||||
/// files encoding
|
||||
#[arg(short = 'c', long)]
|
||||
pub(crate) encoding: Option<String>,
|
||||
|
||||
/// keys subdir
|
||||
#[arg(long, default_value = "keys")]
|
||||
pub(crate) keys_dir: String,
|
||||
|
||||
/// config subdir
|
||||
#[arg(long, default_value = "config")]
|
||||
pub(crate) config_dir: String,
|
||||
|
||||
/// valid days
|
||||
#[arg(long, default_value = "30650")]
|
||||
pub(crate) days: u32,
|
||||
|
||||
/// openssl binary
|
||||
#[arg(long, default_value = "openssl")]
|
||||
pub(crate) openssl: String,
|
||||
|
||||
/// template file
|
||||
#[arg(long, default_value = "template.ovpn")]
|
||||
pub(crate) template_file: String,
|
||||
}
|
||||
|
||||
pub(crate) struct AppConfig {
|
||||
pub(crate) encoding: String,
|
||||
pub(crate) req_days: u32,
|
||||
pub(crate) keys_subdir: String,
|
||||
pub(crate) config_subdir: String,
|
||||
pub(crate) template_file: String,
|
||||
pub(crate) openssl_default_cnf: String,
|
||||
pub(crate) openssl_cnf_env: String,
|
||||
pub(crate) ca_filename: String,
|
||||
pub(crate) default_email_domain: String,
|
||||
pub(crate) openssl: String,
|
||||
pub(crate) base_directory: String,
|
||||
pub(crate) email: String,
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
encoding: "cp866".into(),
|
||||
req_days: 30650,
|
||||
keys_subdir: "keys".into(),
|
||||
config_subdir: "config".into(),
|
||||
template_file: "template.ovpn".into(),
|
||||
openssl_default_cnf: "openssl-1.0.0.cnf".into(),
|
||||
openssl_cnf_env: "KEY_CONFIG".into(),
|
||||
ca_filename: "ca.crt".into(),
|
||||
default_email_domain: "example.com".into(),
|
||||
openssl: "openssl".into(),
|
||||
base_directory: ".".into(),
|
||||
email: "name@example.com".into(),
|
||||
name: "user".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Args> for AppConfig {
|
||||
fn from(args: &Args) -> Self {
|
||||
let defaults = Self::default();
|
||||
|
||||
let base_directory = args
|
||||
.directory
|
||||
.as_ref()
|
||||
.unwrap_or(&defaults.base_directory)
|
||||
.clone();
|
||||
let email = args.email.clone().unwrap_or(format!(
|
||||
"{}@{}",
|
||||
&args.name,
|
||||
defaults.default_email_domain.clone()
|
||||
));
|
||||
let encoding = if let Some(enc) = args.encoding.clone() {
|
||||
enc.to_string()
|
||||
} else {
|
||||
defaults.encoding.clone()
|
||||
};
|
||||
let name = args.name.clone();
|
||||
let openssl = args.openssl.clone();
|
||||
let template_file = args.template_file.clone();
|
||||
let req_days = args.days;
|
||||
let keys_subdir = args.keys_dir.clone();
|
||||
let config_subdir = args.config_dir.clone();
|
||||
|
||||
Self {
|
||||
base_directory,
|
||||
email,
|
||||
encoding,
|
||||
name,
|
||||
openssl,
|
||||
template_file,
|
||||
req_days,
|
||||
keys_subdir,
|
||||
config_subdir,
|
||||
..defaults
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn is_file_exist(filepath: &PathBuf) -> bool {
|
||||
let metadata = tokio::fs::metadata(&filepath).await;
|
||||
if metadata.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !metadata.unwrap().is_file() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) async fn read_file<'a, S, P>(filepath: P, encoding: S) -> Result<String>
|
||||
where
|
||||
S: AsRef<str> + std::cmp::PartialEq<&'a str>,
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let filepath = PathBuf::from(filepath.as_ref());
|
||||
if encoding == "utf8" {
|
||||
return Ok(fs::read_to_string(filepath).await?);
|
||||
}
|
||||
|
||||
let enc = encoding_from_whatwg_label(encoding.as_ref()).ok_or(anyhow!("encoding not found"))?;
|
||||
|
||||
let bytes = fs::read(filepath).await?;
|
||||
enc.decode(&bytes, encoding::DecoderTrap::Ignore)
|
||||
.map_err(|_| anyhow!("could not read file"))
|
||||
}
|
||||
|
||||
pub(crate) async fn write_file(filepath: &PathBuf, text: String, encoding: &str) -> Result<()> {
|
||||
if encoding == "utf8" {
|
||||
return Ok(fs::write(filepath, text).await?);
|
||||
}
|
||||
|
||||
let enc = encoding_from_whatwg_label(encoding).ok_or(anyhow!("encoding not found"))?;
|
||||
let mut bytes = Vec::new();
|
||||
enc.encode_to(&text, EncoderTrap::Ignore, &mut bytes)
|
||||
.map_err(|_| anyhow!("can't encode"))?;
|
||||
|
||||
fs::write(filepath, bytes).await.context("can't write file")
|
||||
}
|
||||
|
||||
pub(crate) async fn read_file_by_lines(
|
||||
filepath: &PathBuf,
|
||||
encoding: &str,
|
||||
) -> Result<Box<dyn Stream<Item = String>>> {
|
||||
Ok(if encoding == "utf8" {
|
||||
let f = File::open(filepath).await?;
|
||||
let reader = BufReader::new(f);
|
||||
let mut lines = reader.lines();
|
||||
Box::new(stream! {
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
yield line
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let text = read_file(filepath, encoding).await?;
|
||||
Box::new(stream! {
|
||||
for line in text.lines() {
|
||||
yield line.to_string()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user