1use std::{
4 collections::{BTreeMap, BTreeSet},
5 fmt::{self, Display},
6 path::PathBuf,
7};
8
9use ave_common::identity::{HashAlgorithm, KeyPairAlgorithm};
10use network::Config as NetworkConfig;
11use serde::{Deserialize, Deserializer, Serialize};
12
13use crate::{helpers::sink::TokenResponse, subject::sinkdata::SinkTypes};
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
17#[serde(default)]
18#[serde(rename_all = "snake_case")]
19pub struct Config {
20 pub keypair_algorithm: KeyPairAlgorithm,
22 pub hash_algorithm: HashAlgorithm,
24 pub internal_db: AveInternalDBConfig,
26 pub external_db: AveExternalDBConfig,
28 pub network: NetworkConfig,
30 pub contracts_path: PathBuf,
32 pub always_accept: bool,
34 pub tracking_size: usize,
36 pub is_service: bool,
38 pub spec: Option<MachineSpec>,
41}
42
43impl Default for Config {
44 fn default() -> Self {
45 Self {
46 keypair_algorithm: KeyPairAlgorithm::Ed25519,
47 hash_algorithm: HashAlgorithm::Blake3,
48 internal_db: Default::default(),
49 external_db: Default::default(),
50 network: Default::default(),
51 contracts_path: PathBuf::new(),
52 always_accept: Default::default(),
53 tracking_size: 100,
54 is_service: false,
55 spec: None,
56 }
57 }
58}
59
60#[derive(Serialize, Deserialize, Debug, Clone)]
68#[serde(rename_all = "snake_case")]
69pub enum MachineSpec {
70 Profile(MachineProfile),
72 Custom {
74 ram_mb: u64,
76 cpu_cores: usize,
78 },
79}
80
81#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
95#[serde(rename_all = "snake_case")]
96pub enum MachineProfile {
97 Nano,
99 Micro,
101 Small,
103 Medium,
105 Large,
107 XLarge,
109 #[serde(rename = "2xlarge")]
111 XXLarge,
112}
113
114impl MachineProfile {
115 pub const fn ram_mb(self) -> u64 {
117 match self {
118 Self::Nano => 512,
119 Self::Micro => 1_024,
120 Self::Small => 2_048,
121 Self::Medium => 4_096,
122 Self::Large => 8_192,
123 Self::XLarge => 16_384,
124 Self::XXLarge => 32_768,
125 }
126 }
127
128 pub const fn cpu_cores(self) -> usize {
130 match self {
131 Self::Nano => 2,
132 Self::Micro => 2,
133 Self::Small => 2,
134 Self::Medium => 2,
135 Self::Large => 2,
136 Self::XLarge => 4,
137 Self::XXLarge => 8,
138 }
139 }
140}
141
142impl Display for MachineProfile {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 match self {
145 Self::Nano => write!(f, "nano"),
146 Self::Micro => write!(f, "micro"),
147 Self::Small => write!(f, "small"),
148 Self::Medium => write!(f, "medium"),
149 Self::Large => write!(f, "large"),
150 Self::XLarge => write!(f, "xlarge"),
151 Self::XXLarge => write!(f, "2xlarge"),
152 }
153 }
154}
155
156pub struct ResolvedSpec {
160 pub ram_mb: u64,
162 pub cpu_cores: usize,
164}
165
166pub fn resolve_spec(spec: Option<&MachineSpec>) -> ResolvedSpec {
172 match spec {
173 Some(MachineSpec::Profile(p)) => ResolvedSpec {
174 ram_mb: p.ram_mb(),
175 cpu_cores: p.cpu_cores(),
176 },
177 Some(MachineSpec::Custom { ram_mb, cpu_cores }) => ResolvedSpec {
178 ram_mb: *ram_mb,
179 cpu_cores: *cpu_cores,
180 },
181 None => ResolvedSpec {
182 ram_mb: detect_ram_mb(),
183 cpu_cores: detect_cpu_cores(),
184 },
185 }
186}
187
188pub(crate) fn detect_ram_mb() -> u64 {
189 #[cfg(target_os = "linux")]
190 {
191 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
192 for line in meminfo.lines() {
193 if let Some(rest) = line.strip_prefix("MemTotal:")
194 && let Some(kb_str) = rest.split_whitespace().next()
195 && let Ok(kb) = kb_str.parse::<u64>()
196 {
197 return kb / 1024;
198 }
199 }
200 }
201 }
202 4_096
203}
204
205pub(crate) fn detect_cpu_cores() -> usize {
206 std::thread::available_parallelism()
207 .map(|n| n.get())
208 .unwrap_or(2)
209}
210
211impl From<MachineProfile> for network::MachineProfile {
214 fn from(p: MachineProfile) -> Self {
215 match p {
216 MachineProfile::Nano => Self::Nano,
217 MachineProfile::Micro => Self::Micro,
218 MachineProfile::Small => Self::Small,
219 MachineProfile::Medium => Self::Medium,
220 MachineProfile::Large => Self::Large,
221 MachineProfile::XLarge => Self::XLarge,
222 MachineProfile::XXLarge => Self::XXLarge,
223 }
224 }
225}
226
227impl From<MachineSpec> for network::MachineSpec {
228 fn from(spec: MachineSpec) -> Self {
229 match spec {
230 MachineSpec::Profile(p) => Self::Profile(p.into()),
231 MachineSpec::Custom { ram_mb, cpu_cores } => {
232 Self::Custom { ram_mb, cpu_cores }
233 }
234 }
235 }
236}
237
238impl From<MachineProfile> for ave_actors::MachineProfile {
239 fn from(p: MachineProfile) -> Self {
240 match p {
241 MachineProfile::Nano => Self::Nano,
242 MachineProfile::Micro => Self::Micro,
243 MachineProfile::Small => Self::Small,
244 MachineProfile::Medium => Self::Medium,
245 MachineProfile::Large => Self::Large,
246 MachineProfile::XLarge => Self::XLarge,
247 MachineProfile::XXLarge => Self::XXLarge,
248 }
249 }
250}
251
252impl From<MachineSpec> for ave_actors::MachineSpec {
253 fn from(spec: MachineSpec) -> Self {
254 match spec {
255 MachineSpec::Profile(p) => Self::Profile(p.into()),
256 MachineSpec::Custom { ram_mb, cpu_cores } => {
257 Self::Custom { ram_mb, cpu_cores }
258 }
259 }
260 }
261}
262
263#[derive(Debug, Clone, Deserialize, Serialize, Default)]
264#[serde(default)]
265pub struct AveInternalDBConfig {
266 #[serde(deserialize_with = "AveInternalDBFeatureConfig::deserialize_db")]
267 pub db: AveInternalDBFeatureConfig,
268 pub durability: bool,
269}
270
271#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
273pub enum AveInternalDBFeatureConfig {
274 #[cfg(feature = "rocksdb")]
276 Rocksdb {
277 path: PathBuf,
279 },
280 #[cfg(feature = "sqlite")]
282 Sqlite {
283 path: PathBuf,
285 },
286}
287
288impl Default for AveInternalDBFeatureConfig {
289 fn default() -> Self {
290 #[cfg(feature = "rocksdb")]
291 return AveInternalDBFeatureConfig::Rocksdb {
292 path: PathBuf::from("db").join("local").join("rocksdb"),
293 };
294 #[cfg(feature = "sqlite")]
295 return Self::Sqlite {
296 path: PathBuf::from("db").join("local").join("sqlite"),
297 };
298 }
299}
300
301impl AveInternalDBFeatureConfig {
302 pub fn build(path: &PathBuf) -> Self {
303 #[cfg(feature = "rocksdb")]
304 return AveInternalDBFeatureConfig::Rocksdb {
305 path: path.to_owned(),
306 };
307 #[cfg(feature = "sqlite")]
308 return Self::Sqlite {
309 path: path.to_owned(),
310 };
311 }
312
313 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
314 where
315 D: Deserializer<'de>,
316 {
317 let path: String = String::deserialize(deserializer)?;
318 #[cfg(feature = "rocksdb")]
319 return Ok(AveInternalDBFeatureConfig::Rocksdb {
320 path: PathBuf::from(path),
321 });
322 #[cfg(feature = "sqlite")]
323 return Ok(Self::Sqlite {
324 path: PathBuf::from(path),
325 });
326 }
327}
328
329impl fmt::Display for AveInternalDBFeatureConfig {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 #[cfg(feature = "rocksdb")]
333 AveInternalDBFeatureConfig::Rocksdb { .. } => write!(f, "Rocksdb"),
334 #[cfg(feature = "sqlite")]
335 Self::Sqlite { .. } => write!(f, "Sqlite"),
336 }
337 }
338}
339
340#[derive(Debug, Clone, Deserialize, Serialize, Default)]
341#[serde(default)]
342pub struct AveExternalDBConfig {
343 #[serde(deserialize_with = "AveExternalDBFeatureConfig::deserialize_db")]
344 pub db: AveExternalDBFeatureConfig,
345 pub durability: bool,
346}
347
348#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
350pub enum AveExternalDBFeatureConfig {
351 #[cfg(feature = "ext-sqlite")]
353 Sqlite {
354 path: PathBuf,
356 },
357}
358
359impl Default for AveExternalDBFeatureConfig {
360 fn default() -> Self {
361 #[cfg(feature = "ext-sqlite")]
362 return Self::Sqlite {
363 path: PathBuf::from("db").join("ext").join("sqlite"),
364 };
365 }
366}
367
368impl AveExternalDBFeatureConfig {
369 pub fn build(path: &PathBuf) -> Self {
370 #[cfg(feature = "ext-sqlite")]
371 return Self::Sqlite {
372 path: path.to_owned(),
373 };
374 }
375
376 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
377 where
378 D: Deserializer<'de>,
379 {
380 let path: String = String::deserialize(deserializer)?;
381 #[cfg(feature = "ext-sqlite")]
382 return Ok(Self::Sqlite {
383 path: PathBuf::from(path),
384 });
385 }
386}
387
388impl fmt::Display for AveExternalDBFeatureConfig {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 write!(f, "Sqlite")
391 }
392}
393
394#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
395pub struct LoggingOutput {
396 pub stdout: bool,
397 pub file: bool,
398 pub api: bool,
399}
400
401impl Default for LoggingOutput {
402 fn default() -> Self {
403 Self {
404 stdout: true,
405 file: Default::default(),
406 api: Default::default(),
407 }
408 }
409}
410
411#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Default, Serialize)]
412#[serde(rename_all = "lowercase")]
413pub enum LoggingRotation {
414 #[default]
415 Size,
416 Hourly,
417 Daily,
418 Weekly,
419 Monthly,
420 Yearly,
421 Never,
422}
423
424impl Display for LoggingRotation {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match self {
427 Self::Size => write!(f, "size"),
428 Self::Hourly => write!(f, "hourly"),
429 Self::Daily => write!(f, "daily"),
430 Self::Weekly => write!(f, "weekly"),
431 Self::Monthly => write!(f, "monthly"),
432 Self::Yearly => write!(f, "yearly"),
433 Self::Never => write!(f, "never"),
434 }
435 }
436}
437
438#[derive(Clone, Debug, Deserialize, Serialize)]
439#[serde(default)]
440pub struct LoggingConfig {
441 pub output: LoggingOutput,
442 pub api_url: Option<String>,
443 pub file_path: PathBuf, pub rotation: LoggingRotation,
445 pub max_size: usize, pub max_files: usize, pub level: String,
451}
452
453impl Default for LoggingConfig {
454 fn default() -> Self {
455 Self {
456 output: LoggingOutput::default(),
457 api_url: None,
458 file_path: PathBuf::from("logs"),
459 rotation: LoggingRotation::default(),
460 max_size: 100 * 1024 * 1024,
461 max_files: 3,
462 level: "info".to_string(),
463 }
464 }
465}
466
467impl LoggingConfig {
468 pub const fn logs(&self) -> bool {
469 self.output.api || self.output.file || self.output.stdout
470 }
471}
472
473#[derive(Clone, Debug, Deserialize, Default, Eq, PartialEq, Serialize)]
474#[serde(default)]
475pub struct SinkServer {
476 pub server: String,
477 pub events: BTreeSet<SinkTypes>,
478 pub url: String,
479 pub auth: bool,
480 #[serde(default = "default_sink_concurrency")]
481 pub concurrency: usize,
482 #[serde(default = "default_sink_queue_capacity")]
483 pub queue_capacity: usize,
484 #[serde(default)]
485 pub queue_policy: SinkQueuePolicy,
486 #[serde(default)]
487 pub routing_strategy: SinkRoutingStrategy,
488 #[serde(default = "default_sink_connect_timeout_ms")]
489 pub connect_timeout_ms: u64,
490 #[serde(default = "default_sink_request_timeout_ms")]
491 pub request_timeout_ms: u64,
492 #[serde(default = "default_sink_max_retries")]
493 pub max_retries: usize,
494}
495
496#[derive(Default)]
497pub struct SinkAuth {
498 pub sink: SinkConfig,
499 pub token: Option<TokenResponse>,
500 pub password: String,
501 pub api_key: String,
502}
503
504#[derive(Clone, Debug, Deserialize, Default, Serialize)]
505#[serde(default)]
506pub struct SinkConfig {
507 pub sinks: BTreeMap<String, Vec<SinkServer>>,
508 pub auth: String,
509 pub username: String,
510}
511
512#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
513#[serde(rename_all = "snake_case")]
514pub enum SinkQueuePolicy {
515 DropOldest,
516 #[default]
517 DropNewest,
518}
519
520#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
521#[serde(rename_all = "snake_case")]
522pub enum SinkRoutingStrategy {
523 #[default]
524 OrderedBySubject,
525 UnorderedRoundRobin,
526}
527
528const fn default_sink_concurrency() -> usize {
529 2
530}
531
532const fn default_sink_queue_capacity() -> usize {
533 1024
534}
535
536const fn default_sink_connect_timeout_ms() -> u64 {
537 2_000
538}
539
540const fn default_sink_request_timeout_ms() -> u64 {
541 5_000
542}
543
544const fn default_sink_max_retries() -> usize {
545 2
546}