1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
// src/config.rs
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use anyhow::{Context, Result};
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct AppConfig {
#[serde(default)]
pub api: ApiConfig,
#[serde(default)]
pub processing: ProcessingConfig,
#[serde(default)]
pub output: OutputConfig,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ApiConfig {
pub base_url: String,
pub api_key: Option<String>,
pub timeout_seconds: u64,
pub max_retries: u32,
}
impl Default for ApiConfig {
fn default() -> Self {
Self {
base_url: "https://api.example.com".to_string(),
api_key: None,
timeout_seconds: 30,
max_retries: 3,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ProcessingConfig {
pub parallel: bool,
pub max_workers: usize,
pub chunk_size: usize,
}
impl Default for ProcessingConfig {
fn default() -> Self {
Self {
parallel: true,
max_workers: num_cpus::get(),
chunk_size: 1024 * 1024, // 1MB
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct OutputConfig {
pub format: OutputFormat,
pub color: bool,
pub quiet: bool,
}
#[derive(Debug, Deserialize, Serialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
#[default]
Text,
Json,
Csv,
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
format: OutputFormat::Text,
color: true,
quiet: false,
}
}
}
pub fn load_config(path: Option<&std::path::Path>) -> Result<AppConfig> {
let builder = config::Config::builder();
// デフォルト設定
let builder = builder.add_source(config::File::from_str(
include_str!("../config/default.toml"),
config::FileFormat::Toml,
));
// ユーザー設定ファイル
let config_path = path
.map(PathBuf::from)
.or_else(|| {
dirs::config_dir().map(|p| p.join("mytools").join("config.toml"))
});
let builder = if let Some(ref path) = config_path {
if path.exists() {
builder.add_source(config::File::from(path.as_path()))
} else {
builder
}
} else {
builder
};
// 環境変数からの上書き
let builder = builder.add_source(
config::Environment::with_prefix("MYTOOLS")
.separator("__")
.try_parsing(true),
);
let config = builder
.build()
.context("Failed to build configuration")?
.try_deserialize()
.context("Failed to deserialize configuration")?;
Ok(config)
}
|