1use std::path::Path;
8use std::time::Duration;
9
10use anyhow::{Context, Result};
11use figment::Figment;
12use figment::providers::{Env, Format, Toml};
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17#[serde(default)]
18pub struct FolkConfig {
19 pub server: ServerConfig,
20 pub workers: WorkersConfig,
21 pub log: LogConfig,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(default)]
26pub struct ServerConfig {
27 pub rpc_socket: String,
29 #[serde(with = "humantime_serde")]
31 pub shutdown_timeout: Duration,
32}
33
34impl Default for ServerConfig {
35 fn default() -> Self {
36 Self {
37 rpc_socket: "/tmp/folk.sock".into(),
38 shutdown_timeout: Duration::from_secs(30),
39 }
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44#[serde(default)]
45pub struct WorkersConfig {
46 pub script: String,
48 pub php: String,
50 pub count: usize,
52 pub max_jobs: u64,
54 #[serde(with = "humantime_serde")]
56 pub ttl: Duration,
57 pub max_memory_mb: u64,
59 #[serde(with = "humantime_serde")]
61 pub exec_timeout: Duration,
62 #[serde(with = "humantime_serde")]
64 pub boot_timeout: Duration,
65}
66
67impl Default for WorkersConfig {
68 fn default() -> Self {
69 Self {
70 script: "vendor/bin/folk-worker".into(),
71 php: "php".into(),
72 count: 4,
73 max_jobs: 1000,
74 ttl: Duration::from_secs(3600),
75 max_memory_mb: 256,
76 exec_timeout: Duration::from_secs(30),
77 boot_timeout: Duration::from_secs(30),
78 }
79 }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(default)]
84pub struct LogConfig {
85 pub filter: String,
87 pub format: LogFormat,
89}
90
91impl Default for LogConfig {
92 fn default() -> Self {
93 Self {
94 filter: "info".into(),
95 format: LogFormat::Text,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
101#[serde(rename_all = "snake_case")]
102pub enum LogFormat {
103 Text,
104 Json,
105 Pretty,
106}
107
108impl FolkConfig {
109 pub fn load() -> Result<Self> {
112 Self::load_from(Path::new("folk.toml"))
113 }
114
115 pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
117 let path = path.as_ref();
118 let mut fig = Figment::from(figment::providers::Serialized::defaults(Self::default()));
119 if path.exists() {
120 fig = fig.merge(Toml::file(path));
121 }
122 fig = fig.merge(Env::prefixed("FOLK_").split("_"));
123 fig.extract().context("failed to parse Folk configuration")
124 }
125}