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 safe_mode: bool,
36 pub tracking_size: usize,
38 pub is_service: bool,
40 pub sync: SyncConfig,
42 pub spec: Option<MachineSpec>,
45}
46
47impl Default for Config {
48 fn default() -> Self {
49 Self {
50 keypair_algorithm: KeyPairAlgorithm::Ed25519,
51 hash_algorithm: HashAlgorithm::Blake3,
52 internal_db: Default::default(),
53 external_db: Default::default(),
54 network: Default::default(),
55 contracts_path: PathBuf::new(),
56 always_accept: Default::default(),
57 safe_mode: false,
58 tracking_size: 100,
59 is_service: false,
60 sync: Default::default(),
61 spec: None,
62 }
63 }
64}
65
66#[derive(Clone, Debug, Default, Deserialize, Serialize)]
67#[serde(default)]
68#[serde(rename_all = "snake_case")]
69pub struct SyncConfig {
70 pub governance: GovernanceSyncConfig,
71 pub tracker: TrackerSyncConfig,
72}
73
74#[derive(Clone, Debug, Deserialize, Serialize)]
75#[serde(default)]
76#[serde(rename_all = "snake_case")]
77pub struct GovernanceSyncConfig {
78 pub interval_secs: u64,
80 pub sample_size: usize,
82 pub response_timeout_secs: u64,
84}
85
86impl Default for GovernanceSyncConfig {
87 fn default() -> Self {
88 Self {
89 interval_secs: 60,
90 sample_size: 3,
91 response_timeout_secs: 10,
92 }
93 }
94}
95
96#[derive(Clone, Debug, Deserialize, Serialize)]
97#[serde(default)]
98#[serde(rename_all = "snake_case")]
99pub struct TrackerSyncConfig {
100 pub interval_secs: u64,
102 pub page_size: usize,
104 pub response_timeout_secs: u64,
106 pub update_batch_size: usize,
108 pub update_timeout_secs: u64,
110}
111
112impl Default for TrackerSyncConfig {
113 fn default() -> Self {
114 Self {
115 interval_secs: 30,
116 page_size: 50,
117 response_timeout_secs: 10,
118 update_batch_size: 2,
119 update_timeout_secs: 10,
120 }
121 }
122}
123
124#[derive(Serialize, Deserialize, Debug, Clone)]
132#[serde(rename_all = "snake_case")]
133pub enum MachineSpec {
134 Profile(MachineProfile),
136 Custom {
138 ram_mb: u64,
140 cpu_cores: usize,
142 },
143}
144
145#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
159#[serde(rename_all = "snake_case")]
160pub enum MachineProfile {
161 Nano,
163 Micro,
165 Small,
167 Medium,
169 Large,
171 XLarge,
173 #[serde(rename = "2xlarge")]
175 XXLarge,
176}
177
178impl MachineProfile {
179 pub const fn ram_mb(self) -> u64 {
181 match self {
182 Self::Nano => 512,
183 Self::Micro => 1_024,
184 Self::Small => 2_048,
185 Self::Medium => 4_096,
186 Self::Large => 8_192,
187 Self::XLarge => 16_384,
188 Self::XXLarge => 32_768,
189 }
190 }
191
192 pub const fn cpu_cores(self) -> usize {
194 match self {
195 Self::Nano => 2,
196 Self::Micro => 2,
197 Self::Small => 2,
198 Self::Medium => 2,
199 Self::Large => 2,
200 Self::XLarge => 4,
201 Self::XXLarge => 8,
202 }
203 }
204}
205
206impl Display for MachineProfile {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Self::Nano => write!(f, "nano"),
210 Self::Micro => write!(f, "micro"),
211 Self::Small => write!(f, "small"),
212 Self::Medium => write!(f, "medium"),
213 Self::Large => write!(f, "large"),
214 Self::XLarge => write!(f, "xlarge"),
215 Self::XXLarge => write!(f, "2xlarge"),
216 }
217 }
218}
219
220pub struct ResolvedSpec {
224 pub ram_mb: u64,
226 pub cpu_cores: usize,
228}
229
230pub fn resolve_spec(spec: Option<&MachineSpec>) -> ResolvedSpec {
236 match spec {
237 Some(MachineSpec::Profile(p)) => ResolvedSpec {
238 ram_mb: p.ram_mb(),
239 cpu_cores: p.cpu_cores(),
240 },
241 Some(MachineSpec::Custom { ram_mb, cpu_cores }) => ResolvedSpec {
242 ram_mb: *ram_mb,
243 cpu_cores: *cpu_cores,
244 },
245 None => ResolvedSpec {
246 ram_mb: detect_ram_mb(),
247 cpu_cores: detect_cpu_cores(),
248 },
249 }
250}
251
252pub(crate) fn detect_ram_mb() -> u64 {
253 #[cfg(target_os = "linux")]
254 {
255 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
256 for line in meminfo.lines() {
257 if let Some(rest) = line.strip_prefix("MemTotal:")
258 && let Some(kb_str) = rest.split_whitespace().next()
259 && let Ok(kb) = kb_str.parse::<u64>()
260 {
261 return kb / 1024;
262 }
263 }
264 }
265 }
266 4_096
267}
268
269pub(crate) fn detect_cpu_cores() -> usize {
270 std::thread::available_parallelism()
271 .map(|n| n.get())
272 .unwrap_or(2)
273}
274
275impl From<MachineProfile> for network::MachineProfile {
278 fn from(p: MachineProfile) -> Self {
279 match p {
280 MachineProfile::Nano => Self::Nano,
281 MachineProfile::Micro => Self::Micro,
282 MachineProfile::Small => Self::Small,
283 MachineProfile::Medium => Self::Medium,
284 MachineProfile::Large => Self::Large,
285 MachineProfile::XLarge => Self::XLarge,
286 MachineProfile::XXLarge => Self::XXLarge,
287 }
288 }
289}
290
291impl From<MachineSpec> for network::MachineSpec {
292 fn from(spec: MachineSpec) -> Self {
293 match spec {
294 MachineSpec::Profile(p) => Self::Profile(p.into()),
295 MachineSpec::Custom { ram_mb, cpu_cores } => {
296 Self::Custom { ram_mb, cpu_cores }
297 }
298 }
299 }
300}
301
302impl From<MachineProfile> for ave_actors::MachineProfile {
303 fn from(p: MachineProfile) -> Self {
304 match p {
305 MachineProfile::Nano => Self::Nano,
306 MachineProfile::Micro => Self::Micro,
307 MachineProfile::Small => Self::Small,
308 MachineProfile::Medium => Self::Medium,
309 MachineProfile::Large => Self::Large,
310 MachineProfile::XLarge => Self::XLarge,
311 MachineProfile::XXLarge => Self::XXLarge,
312 }
313 }
314}
315
316impl From<MachineSpec> for ave_actors::MachineSpec {
317 fn from(spec: MachineSpec) -> Self {
318 match spec {
319 MachineSpec::Profile(p) => Self::Profile(p.into()),
320 MachineSpec::Custom { ram_mb, cpu_cores } => {
321 Self::Custom { ram_mb, cpu_cores }
322 }
323 }
324 }
325}
326
327#[derive(Debug, Clone, Deserialize, Serialize, Default)]
328#[serde(default)]
329pub struct AveInternalDBConfig {
330 #[serde(deserialize_with = "AveInternalDBFeatureConfig::deserialize_db")]
331 pub db: AveInternalDBFeatureConfig,
332 pub durability: bool,
333}
334
335#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
337pub enum AveInternalDBFeatureConfig {
338 #[cfg(feature = "rocksdb")]
340 Rocksdb {
341 path: PathBuf,
343 },
344 #[cfg(feature = "sqlite")]
346 Sqlite {
347 path: PathBuf,
349 },
350}
351
352impl Default for AveInternalDBFeatureConfig {
353 fn default() -> Self {
354 #[cfg(feature = "rocksdb")]
355 return AveInternalDBFeatureConfig::Rocksdb {
356 path: PathBuf::from("db").join("local").join("rocksdb"),
357 };
358 #[cfg(feature = "sqlite")]
359 return Self::Sqlite {
360 path: PathBuf::from("db").join("local").join("sqlite"),
361 };
362 }
363}
364
365impl AveInternalDBFeatureConfig {
366 pub fn build(path: &PathBuf) -> Self {
367 #[cfg(feature = "rocksdb")]
368 return AveInternalDBFeatureConfig::Rocksdb {
369 path: path.to_owned(),
370 };
371 #[cfg(feature = "sqlite")]
372 return Self::Sqlite {
373 path: path.to_owned(),
374 };
375 }
376
377 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
378 where
379 D: Deserializer<'de>,
380 {
381 let path: String = String::deserialize(deserializer)?;
382 #[cfg(feature = "rocksdb")]
383 return Ok(AveInternalDBFeatureConfig::Rocksdb {
384 path: PathBuf::from(path),
385 });
386 #[cfg(feature = "sqlite")]
387 return Ok(Self::Sqlite {
388 path: PathBuf::from(path),
389 });
390 }
391}
392
393impl fmt::Display for AveInternalDBFeatureConfig {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 match self {
396 #[cfg(feature = "rocksdb")]
397 AveInternalDBFeatureConfig::Rocksdb { .. } => write!(f, "Rocksdb"),
398 #[cfg(feature = "sqlite")]
399 Self::Sqlite { .. } => write!(f, "Sqlite"),
400 }
401 }
402}
403
404#[derive(Debug, Clone, Deserialize, Serialize, Default)]
405#[serde(default)]
406pub struct AveExternalDBConfig {
407 #[serde(deserialize_with = "AveExternalDBFeatureConfig::deserialize_db")]
408 pub db: AveExternalDBFeatureConfig,
409 pub durability: bool,
410}
411
412#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
414pub enum AveExternalDBFeatureConfig {
415 #[cfg(feature = "ext-sqlite")]
417 Sqlite {
418 path: PathBuf,
420 },
421}
422
423impl Default for AveExternalDBFeatureConfig {
424 fn default() -> Self {
425 #[cfg(feature = "ext-sqlite")]
426 return Self::Sqlite {
427 path: PathBuf::from("db").join("ext").join("sqlite"),
428 };
429 }
430}
431
432impl AveExternalDBFeatureConfig {
433 pub fn build(path: &PathBuf) -> Self {
434 #[cfg(feature = "ext-sqlite")]
435 return Self::Sqlite {
436 path: path.to_owned(),
437 };
438 }
439
440 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
441 where
442 D: Deserializer<'de>,
443 {
444 let path: String = String::deserialize(deserializer)?;
445 #[cfg(feature = "ext-sqlite")]
446 return Ok(Self::Sqlite {
447 path: PathBuf::from(path),
448 });
449 }
450}
451
452impl fmt::Display for AveExternalDBFeatureConfig {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 write!(f, "Sqlite")
455 }
456}
457
458#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
459pub struct LoggingOutput {
460 pub stdout: bool,
461 pub file: bool,
462 pub api: bool,
463}
464
465impl Default for LoggingOutput {
466 fn default() -> Self {
467 Self {
468 stdout: true,
469 file: Default::default(),
470 api: Default::default(),
471 }
472 }
473}
474
475#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Default, Serialize)]
476#[serde(rename_all = "lowercase")]
477pub enum LoggingRotation {
478 #[default]
479 Size,
480 Hourly,
481 Daily,
482 Weekly,
483 Monthly,
484 Yearly,
485 Never,
486}
487
488impl Display for LoggingRotation {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 match self {
491 Self::Size => write!(f, "size"),
492 Self::Hourly => write!(f, "hourly"),
493 Self::Daily => write!(f, "daily"),
494 Self::Weekly => write!(f, "weekly"),
495 Self::Monthly => write!(f, "monthly"),
496 Self::Yearly => write!(f, "yearly"),
497 Self::Never => write!(f, "never"),
498 }
499 }
500}
501
502#[derive(Clone, Debug, Deserialize, Serialize)]
503#[serde(default)]
504pub struct LoggingConfig {
505 pub output: LoggingOutput,
506 pub api_url: Option<String>,
507 pub file_path: PathBuf, pub rotation: LoggingRotation,
509 pub max_size: usize, pub max_files: usize, pub level: String,
515}
516
517impl Default for LoggingConfig {
518 fn default() -> Self {
519 Self {
520 output: LoggingOutput::default(),
521 api_url: None,
522 file_path: PathBuf::from("logs"),
523 rotation: LoggingRotation::default(),
524 max_size: 100 * 1024 * 1024,
525 max_files: 3,
526 level: "info".to_string(),
527 }
528 }
529}
530
531impl LoggingConfig {
532 pub const fn logs(&self) -> bool {
533 self.output.api || self.output.file || self.output.stdout
534 }
535}
536
537#[derive(Clone, Debug, Deserialize, Default, Eq, PartialEq, Serialize)]
538#[serde(default)]
539pub struct SinkServer {
540 pub server: String,
541 pub events: BTreeSet<SinkTypes>,
542 pub url: String,
543 pub auth: bool,
544 #[serde(default = "default_sink_concurrency")]
545 pub concurrency: usize,
546 #[serde(default = "default_sink_queue_capacity")]
547 pub queue_capacity: usize,
548 #[serde(default)]
549 pub queue_policy: SinkQueuePolicy,
550 #[serde(default)]
551 pub routing_strategy: SinkRoutingStrategy,
552 #[serde(default = "default_sink_connect_timeout_ms")]
553 pub connect_timeout_ms: u64,
554 #[serde(default = "default_sink_request_timeout_ms")]
555 pub request_timeout_ms: u64,
556 #[serde(default = "default_sink_max_retries")]
557 pub max_retries: usize,
558}
559
560#[derive(Default)]
561pub struct SinkAuth {
562 pub sink: SinkConfig,
563 pub token: Option<TokenResponse>,
564 pub password: String,
565 pub api_key: String,
566}
567
568#[derive(Clone, Debug, Deserialize, Default, Serialize)]
569#[serde(default)]
570pub struct SinkConfig {
571 pub sinks: BTreeMap<String, Vec<SinkServer>>,
572 pub auth: String,
573 pub username: String,
574}
575
576#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
577#[serde(rename_all = "snake_case")]
578pub enum SinkQueuePolicy {
579 DropOldest,
580 #[default]
581 DropNewest,
582}
583
584#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
585#[serde(rename_all = "snake_case")]
586pub enum SinkRoutingStrategy {
587 #[default]
588 OrderedBySubject,
589 UnorderedRoundRobin,
590}
591
592const fn default_sink_concurrency() -> usize {
593 2
594}
595
596const fn default_sink_queue_capacity() -> usize {
597 1024
598}
599
600const fn default_sink_connect_timeout_ms() -> u64 {
601 2_000
602}
603
604const fn default_sink_request_timeout_ms() -> u64 {
605 5_000
606}
607
608const fn default_sink_max_retries() -> usize {
609 2
610}