Skip to content

Commit b99392f

Browse files
committed
refactor read config
1 parent 131ffd0 commit b99392f

File tree

18 files changed

+205
-187
lines changed

18 files changed

+205
-187
lines changed

Cargo.toml

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ description = "transfer file"
1212
documentation = ""
1313
edition = "2021"
1414
homepage = "https://github.com/robatipoor/pf"
15-
keywords = ["paste","file","transfer"]
15+
keywords = ["paste", "file", "transfer"]
1616
license = "MIT"
1717
readme = "./README.md"
1818
repository = "https://github.com/robatipoor/pf"
@@ -27,43 +27,54 @@ strip = true
2727
anyhow = "1.0.79"
2828
argon2 = "0.5.2"
2929
assert_cmd = "2.0.13"
30-
async-stream = {version = "0.3.5"}
30+
async-stream = { version = "0.3.5" }
3131
async-trait = "0.1.77"
32-
axum = {version = "0.7.4", features = ["multipart"]}
33-
axum-extra = {version = "0.9.2", features = ["async-read-body"]}
32+
axum = { version = "0.7.4", features = ["multipart"] }
33+
axum-extra = { version = "0.9.2", features = ["async-read-body"] }
3434
base64 = "0.21.7"
3535
bincode = "1.3.3"
3636
build_html = "2.4.0"
37-
chrono = {version = "0.4.31", features = ["serde"]}
38-
clap = {version = "4.4.18", features = ["derive"]}
39-
config = {version = "0.13.4", default-features = false, features = ["toml"]}
37+
chrono = { version = "0.4.31", features = ["serde"] }
38+
clap = { version = "4.4.18", features = ["derive"] }
39+
config = { version = "0.13.4", default-features = false, features = ["toml"] }
4040
cuid2 = "0.1.2"
41-
fake = {version = "2.9.2", features = ['derive', 'uuid', 'chrono']}
41+
fake = { version = "2.9.2", features = ['derive', 'uuid', 'chrono'] }
4242
futures-util = "0.3.30"
43-
wiremock = {version = "0.5.22"}
43+
wiremock = { version = "0.5.22" }
4444
hyper = "1.1.0"
4545
indicatif = "0.17.7"
4646
log = "0.4.20"
4747
log-derive = "0.4.1"
4848
mime_guess = "2.0.4"
4949
multer = "3.0.0"
50-
once_cell = {version = "1.19.0"}
50+
once_cell = { version = "1.19.0" }
5151
project-root = "0.2.2"
5252
qrcodegen = "1.8.0"
5353
rand = "0.8.5"
5454
regex = "1.10.2"
55-
reqwest = {version = "0.11.23", default-features = false, features = ["json", "multipart", "stream", "rustls-tls"]}
56-
serde = {version = "1.0.195", features = ["derive"]}
55+
reqwest = { version = "0.11.23", default-features = false, features = [
56+
"json",
57+
"multipart",
58+
"stream",
59+
"rustls-tls",
60+
] }
61+
serde = { version = "1.0.195", features = ["derive"] }
5762
serde_json = "1.0.111"
5863
sled = "0.34.7"
5964
strum = { version = "0.25.0", features = ["derive"] }
6065
test-context = "0.1.4"
6166
thiserror = "1.0.56"
62-
tokio = {version = "1.35.1", features = ["macros", "time", "process", "net", "rt-multi-thread"]}
67+
tokio = { version = "1.35.1", features = [
68+
"macros",
69+
"time",
70+
"process",
71+
"net",
72+
"rt-multi-thread",
73+
] }
6374
tokio-util = "0.7.10"
64-
tower = {version = "0.4.13", features = ["util"]}
65-
tower-http = {version = "0.5.1", features = ["fs"]}
75+
tower = { version = "0.4.13", features = ["util"] }
76+
tower-http = { version = "0.5.1", features = ["fs"] }
6677
tracing = "0.1.40"
6778
tracing-subscriber = "0.3.18"
6879
url = "2.5.0"
69-
validator = {version = "0.16.1", features = ["derive"]}
80+
garde = "0.17.0"

api/Cargo.toml

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,36 @@ name = "api"
88
path = "src/bin/main.rs"
99

1010
[dependencies]
11-
sdk = {path = "../sdk"}
12-
anyhow = {workspace = true}
13-
argon2 = {workspace = true}
14-
async-trait = {workspace = true}
15-
clap = {workspace = true}
16-
axum = {workspace = true}
17-
axum-extra = {workspace = true}
18-
bincode = {workspace = true}
19-
build_html = {workspace = true}
20-
chrono = {workspace = true}
21-
config = {workspace = true}
22-
cuid2 = {workspace = true}
23-
fake = {workspace = true}
24-
futures-util = {workspace = true}
25-
hyper = {workspace = true}
26-
once_cell = {workspace = true}
27-
qrcodegen = {workspace = true}
28-
rand = {workspace = true}
29-
reqwest = {workspace = true}
30-
serde = {workspace = true}
31-
serde_json = {workspace = true}
32-
sled = {workspace = true}
33-
strum = {workspace = true}
34-
test-context = {workspace = true}
35-
thiserror = {workspace = true}
36-
tokio = {workspace = true}
37-
tokio-util = {workspace = true}
38-
tower = {workspace = true}
39-
tower-http = {workspace = true}
40-
tracing = {workspace = true}
41-
tracing-subscriber = {workspace = true}
42-
url = {workspace = true}
43-
validator = {workspace = true}
11+
sdk = { path = "../sdk" }
12+
anyhow = { workspace = true }
13+
argon2 = { workspace = true }
14+
async-trait = { workspace = true }
15+
clap = { workspace = true }
16+
axum = { workspace = true }
17+
axum-extra = { workspace = true }
18+
bincode = { workspace = true }
19+
build_html = { workspace = true }
20+
chrono = { workspace = true }
21+
config = { workspace = true }
22+
cuid2 = { workspace = true }
23+
fake = { workspace = true }
24+
futures-util = { workspace = true }
25+
hyper = { workspace = true }
26+
once_cell = { workspace = true }
27+
qrcodegen = { workspace = true }
28+
rand = { workspace = true }
29+
reqwest = { workspace = true }
30+
serde = { workspace = true }
31+
serde_json = { workspace = true }
32+
sled = { workspace = true }
33+
strum = { workspace = true }
34+
test-context = { workspace = true }
35+
thiserror = { workspace = true }
36+
tokio = { workspace = true }
37+
tokio-util = { workspace = true }
38+
tower = { workspace = true }
39+
tower-http = { workspace = true }
40+
tracing = { workspace = true }
41+
tracing-subscriber = { workspace = true }
42+
url = { workspace = true }
43+
garde = { workspace = true }

api/settings/base.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
domain = "http://127.0.0.1"
22
max_upload_size = 1024
33
default_code_length = 3
4-
default_expire_time = 7200
4+
default_expire_secs = 7200
55

66
[server]
77
schema = "http"

api/src/bin/main.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
use api::{error::ApiResult, server::ApiServer, util::tracing::INIT_SUBSCRIBER};
1+
use api::{
2+
error::ApiResult,
3+
server::ApiServer,
4+
util::{arg::get_env_source, tracing::INIT_SUBSCRIBER},
5+
};
26
use axum::extract::State;
7+
use clap::Parser;
38
use once_cell::sync::Lazy;
49
use tracing::warn;
510

611
#[tokio::main]
712
async fn main() -> ApiResult {
8-
let config = api::configure::ApiConfig::read()?;
13+
let args = api::configure::Args::parse();
14+
let config = api::configure::ApiConfig::read(args.config, get_env_source("PF"))?;
915
Lazy::force(&INIT_SUBSCRIBER);
1016
if let Err(e) = tokio::fs::create_dir_all(&config.fs.base_dir).await {
1117
warn!("failed create base dir: {e}");

api/src/configure/mod.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use crate::util::arg::get_env_source;
2-
use clap::Parser;
3-
use config::ConfigError;
2+
use config::Environment;
43
use once_cell::sync::Lazy;
54
use serde::Deserialize;
65
use std::{
76
net::{AddrParseError, SocketAddr},
87
path::PathBuf,
98
};
109

11-
pub static CONFIG: Lazy<ApiConfig> = Lazy::new(|| ApiConfig::read().unwrap());
10+
pub static CONFIG: Lazy<ApiConfig> =
11+
Lazy::new(|| ApiConfig::read(None, get_env_source("PF")).unwrap());
1212

1313
#[derive(Debug, Deserialize, Clone)]
1414
pub struct ApiConfig {
@@ -59,29 +59,34 @@ impl ServerConfig {
5959
}
6060

6161
impl ApiConfig {
62-
pub fn read() -> Result<Self, config::ConfigError> {
63-
let mut cfg = config::Config::builder();
64-
let env_src = get_env_source("PF");
65-
if let Some(path) = Args::parse().config {
66-
cfg = cfg.add_source(config::File::from(path)).add_source(env_src);
67-
} else {
68-
let base_config = std::env::current_dir()
69-
.map_err(|e| ConfigError::Message(e.to_string()))?
70-
.join("settings")
71-
.join("base.toml");
72-
cfg = cfg
73-
.add_source(config::File::from(base_config))
74-
.add_source(env_src);
75-
}
76-
cfg.build()?.try_deserialize()
62+
pub fn read(
63+
arg_file_src: Option<PathBuf>,
64+
env_src: Environment,
65+
) -> Result<Self, config::ConfigError> {
66+
config::Config::builder()
67+
.add_source(config::File::from(
68+
get_basic_settings_path(arg_file_src)
69+
.map_err(|e| config::ConfigError::Message(e.to_string()))?,
70+
))
71+
.add_source(env_src)
72+
.build()?
73+
.try_deserialize()
7774
}
7875
}
7976

80-
#[derive(clap::Parser, Debug)]
77+
fn get_basic_settings_path(arg_path: Option<PathBuf>) -> std::io::Result<PathBuf> {
78+
if let Some(path) = arg_path {
79+
Ok(path)
80+
} else {
81+
Ok(std::env::current_dir()?.join("settings").join("base.toml"))
82+
}
83+
}
84+
85+
#[derive(clap::Parser, Debug, Default)]
8186
#[command(author, version, about, long_about = None)]
82-
struct Args {
87+
pub struct Args {
8388
#[arg(short, long)]
84-
config: Option<PathBuf>,
89+
pub config: Option<PathBuf>,
8590
}
8691

8792
#[cfg(test)]
@@ -91,7 +96,7 @@ mod tests {
9196

9297
#[test]
9398
fn test_read_config() {
94-
let config = ApiConfig::read();
95-
assert!(config.is_ok());
99+
let env_ns = "TEST_PF";
100+
ApiConfig::read(None, get_env_source(env_ns)).unwrap();
96101
}
97102
}

api/src/database/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl Database {
113113
}
114114
}
115115
Err(err) if err.current.is_some() => {
116-
return Err(ApiError::ResourceExists("File path exists".to_string()));
116+
return Err(ApiError::ResourceExistsError("File path exists".to_string()));
117117
}
118118
Err(err) => {
119119
tracing::error!("Compare and swap error: {err}");

api/src/error/mod.rs

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ pub type ApiResult<T = ()> = std::result::Result<T, ApiError>;
1010
#[derive(Debug, thiserror::Error)]
1111
pub enum ApiError {
1212
#[error(transparent)]
13-
InvalidInput(#[from] validator::ValidationErrors),
13+
InvalidInputError(#[from] garde::Report),
1414
#[error("bad request: {0}")]
15-
BadRequest(String),
15+
BadRequestError(String),
1616
#[error("resource not found: {0}")]
17-
NotFound(String),
17+
NotFoundError(String),
1818
#[error("{0}")]
19-
PermissionDenied(String),
19+
PermissionDeniedError(String),
2020
#[error("resource not available: {0}")]
21-
NotAvailable(String),
21+
NotAvailableError(String),
2222
#[error("resource {0} exists already")]
23-
ResourceExists(String),
23+
ResourceExistsError(String),
2424
#[error(transparent)]
2525
ConfigError(#[from] config::ConfigError),
2626
#[error(transparent)]
@@ -50,23 +50,23 @@ pub enum ApiError {
5050
#[error("duration out of range error: {0}")]
5151
DurationOutOfRangeError(#[from] chrono::OutOfRangeError),
5252
#[error(transparent)]
53-
Unknown(#[from] anyhow::Error),
53+
UnknownError(#[from] anyhow::Error),
5454
}
5555

5656
impl ApiError {
5757
pub fn response(&self) -> (BodyResponseError, StatusCode) {
5858
use ApiError::*;
5959
let (error_type, error_message, status_code) = match self {
60-
InvalidInput(err) => (
60+
InvalidInputError(err) => (
6161
"INVALID_INPUT",
6262
err.to_string(),
6363
StatusCode::UNPROCESSABLE_ENTITY,
6464
),
65-
BadRequest(err) => ("BAD_REQUEST", err.to_string(), StatusCode::BAD_REQUEST),
66-
PermissionDenied(err) => ("PERMISSION_DENIED", err.to_string(), StatusCode::FORBIDDEN),
67-
NotAvailable(err) => ("NOT_AVAILABLE", err.to_string(), StatusCode::NOT_FOUND),
68-
NotFound(err) => ("NOT_FOUND", err.to_string(), StatusCode::NOT_FOUND),
69-
ResourceExists(err) => ("RESOURCE_EXISTS", err.to_string(), StatusCode::CONFLICT),
65+
BadRequestError(err) => ("BAD_REQUEST", err.to_string(), StatusCode::BAD_REQUEST),
66+
PermissionDeniedError(err) => ("PERMISSION_DENIED", err.to_string(), StatusCode::FORBIDDEN),
67+
NotAvailableError(err) => ("NOT_AVAILABLE", err.to_string(), StatusCode::NOT_FOUND),
68+
NotFoundError(err) => ("NOT_FOUND", err.to_string(), StatusCode::NOT_FOUND),
69+
ResourceExistsError(err) => ("RESOURCE_EXISTS", err.to_string(), StatusCode::CONFLICT),
7070
ConfigError(err) => (
7171
"CONFIG_ERROR",
7272
err.to_string(),
@@ -127,7 +127,7 @@ impl ApiError {
127127
err.to_string(),
128128
StatusCode::INTERNAL_SERVER_ERROR,
129129
),
130-
Unknown(err) => (
130+
UnknownError(err) => (
131131
"UNKNOWN_ERROR",
132132
err.to_string(),
133133
StatusCode::INTERNAL_SERVER_ERROR,
@@ -158,16 +158,9 @@ impl IntoResponse for ApiError {
158158
}
159159

160160
pub fn invalid_input_error(field: &'static str, message: &'static str) -> ApiError {
161-
let mut err = validator::ValidationErrors::new();
162-
err.add(
163-
field,
164-
validator::ValidationError {
165-
code: std::borrow::Cow::from("1"),
166-
message: Some(std::borrow::Cow::Borrowed(message)),
167-
params: std::collections::HashMap::new(),
168-
},
169-
);
170-
ApiError::InvalidInput(err)
161+
let mut report = garde::Report::new();
162+
report.append(garde::Path::new(field), garde::Error::new(message));
163+
ApiError::InvalidInputError(report)
171164
}
172165

173166
pub trait ToApiResult<T> {
@@ -176,6 +169,6 @@ pub trait ToApiResult<T> {
176169

177170
impl<T> ToApiResult<T> for Option<T> {
178171
fn to_result(self) -> ApiResult<T> {
179-
self.ok_or_else(|| ApiError::NotFound(format!("{} not found", std::any::type_name::<T>())))
172+
self.ok_or_else(|| ApiError::NotFoundError(format!("{} not found", std::any::type_name::<T>())))
180173
}
181174
}

api/src/handler/file.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use axum::{
77
};
88

99
use sdk::model::{
10-
request::UploadParamQuery,
10+
request::UploadQueryParam,
1111
response::{MessageResponse, MetaDataFileResponse, UploadResponse},
1212
};
1313

14+
use garde::Validate;
1415
use tower::ServiceExt;
1516
use tower_http::services::fs::ServeFileSystemResponseBody;
16-
use validator::Validate;
1717

1818
use crate::{
1919
error::ApiResult,
@@ -23,11 +23,11 @@ use crate::{
2323

2424
pub async fn upload(
2525
State(state): State<ApiState>,
26-
Query(query): Query<UploadParamQuery>,
26+
Query(query): Query<UploadQueryParam>,
2727
headers: HeaderMap,
2828
multipart: Multipart,
2929
) -> ApiResult<Json<UploadResponse>> {
30-
query.validate()?;
30+
query.validate(&())?;
3131
let secret = crate::util::http::parse_basic_auth(&headers)?;
3232
let (path, expire_time) = service::file::store(&state, &query, secret, multipart).await?;
3333
let url = path.url(&state.config.domain);

0 commit comments

Comments
 (0)