1use serde::Deserialize;
2use std::path::PathBuf;
3use anyhow::{Result, Context};
4
5#[derive(Debug, Deserialize, Clone)]
6pub struct Config {
7 #[serde(default)]
8 pub server: ServerConfig,
9 pub rcon: RconConfig,
10 pub ai: Option<AiConfig>,
11 pub ollama: Option<OllamaConfig>,
12 #[serde(default)]
13 pub backup: BackupConfig,
14 #[serde(default)]
15 pub notification: NotificationConfig,
16}
17
18#[derive(Debug, Deserialize, Clone)]
19pub struct ServerConfig {
20 #[serde(default = "default_jar")]
21 pub jar: String,
22 #[serde(default = "default_min_mem")]
23 pub min_mem: String,
24 #[serde(default = "default_max_mem")]
25 pub max_mem: String,
26 #[serde(default = "default_session")]
27 pub session_name: String,
28 #[serde(default = "default_log_file")]
29 pub log_file: String,
30}
31
32fn default_jar() -> String { "fabric-server.jar".to_string() }
33fn default_min_mem() -> String { "512M".to_string() }
34fn default_max_mem() -> String { "1G".to_string() }
35fn default_session() -> String { "mc_server".to_string() }
36fn default_log_file() -> String { "logs/latest.log".to_string() }
37
38impl Default for ServerConfig {
39 fn default() -> Self {
40 Self {
41 jar: default_jar(),
42 min_mem: default_min_mem(),
43 max_mem: default_max_mem(),
44 session_name: default_session(),
45 log_file: default_log_file(),
46 }
47 }
48}
49
50#[derive(Debug, Deserialize, Clone)]
51pub struct RconConfig {
52 #[serde(default = "default_rcon_host")]
53 pub host: String,
54 #[serde(default = "default_rcon_port")]
55 pub port: u16,
56 pub password: String,
57}
58
59fn default_rcon_host() -> String { "127.0.0.1".to_string() }
60fn default_rcon_port() -> u16 { 25575 }
61
62#[derive(Debug, Deserialize, Clone)]
63pub struct AiConfig {
64 pub api_url: String,
65 pub api_key: String,
66 #[serde(default = "default_model")]
67 pub model: String,
68 #[serde(default = "default_trigger")]
69 pub trigger: String,
70 #[serde(default = "default_max_tokens")]
71 pub max_tokens: u32,
72 #[serde(default = "default_temperature")]
73 pub temperature: f32,
74}
75
76fn default_model() -> String { "gpt-3.5-turbo".to_string() }
77fn default_trigger() -> String { "!".to_string() }
78fn default_max_tokens() -> u32 { 150 }
79fn default_temperature() -> f32 { 0.7 }
80
81#[derive(Debug, Deserialize, Clone)]
82pub struct OllamaConfig {
83 #[serde(default = "default_ollama_enabled")]
84 pub enabled: bool,
85 #[serde(default = "default_ollama_url")]
86 pub url: String,
87 #[serde(default = "default_ollama_model")]
88 pub model: String,
89}
90
91fn default_ollama_enabled() -> bool { false }
92fn default_ollama_url() -> String { "http://localhost:11434/api/generate".to_string() }
93fn default_ollama_model() -> String { "qwen:0.5b".to_string() }
94
95#[derive(Debug, Deserialize, Clone)]
96pub struct BackupConfig {
97 #[serde(default = "default_world_dir")]
98 pub world_dir: String,
99 #[serde(default = "default_backup_dest")]
100 pub backup_dest: String,
101 #[serde(default = "default_retain_days")]
102 pub retain_days: u32,
103}
104
105fn default_world_dir() -> String { "world".to_string() }
106fn default_backup_dest() -> String { "../backups".to_string() }
107fn default_retain_days() -> u32 { 7 }
108
109impl Default for BackupConfig {
110 fn default() -> Self {
111 Self {
112 world_dir: default_world_dir(),
113 backup_dest: default_backup_dest(),
114 retain_days: default_retain_days(),
115 }
116 }
117}
118
119#[derive(Debug, Deserialize, Clone)]
120pub struct NotificationConfig {
121 #[serde(default)]
122 pub telegram_bot_token: String,
123 #[serde(default)]
124 pub telegram_chat_id: String,
125 #[serde(default = "default_termux_notify")]
126 pub termux_notify: bool,
127}
128
129fn default_termux_notify() -> bool { true }
130
131impl Default for NotificationConfig {
132 fn default() -> Self {
133 Self {
134 telegram_bot_token: String::new(),
135 telegram_chat_id: String::new(),
136 termux_notify: default_termux_notify(),
137 }
138 }
139}
140
141impl Config {
142 pub fn load(path: &PathBuf) -> Result<Self> {
143 let content = std::fs::read_to_string(path)
144 .with_context(|| format!("Failed to read config file: {:?}", path))?;
145
146 let config: Config = toml::from_str(&content)
147 .with_context(|| "Failed to parse config file")?;
148
149 Ok(config)
150 }
151
152 pub fn load_from_str(content: &str) -> Result<Self> {
153 let config: Config = toml::from_str(content)
154 .with_context(|| "Failed to parse config content")?;
155 Ok(config)
156 }
157}