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 pub runtime: RuntimeKind,
31 #[serde(with = "humantime_serde")]
33 pub shutdown_timeout: Duration,
34}
35
36impl Default for ServerConfig {
37 fn default() -> Self {
38 Self {
39 rpc_socket: "/tmp/folk.sock".into(),
40 runtime: RuntimeKind::Pipe,
41 shutdown_timeout: Duration::from_secs(30),
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
47#[serde(rename_all = "snake_case")]
48pub enum RuntimeKind {
49 Pipe,
50 Fork,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(default)]
55pub struct WorkersConfig {
56 pub script: String,
58 pub php: String,
60 pub count: usize,
62 pub max_jobs: u64,
64 #[serde(with = "humantime_serde")]
66 pub ttl: Duration,
67 pub max_memory_mb: u64,
69 #[serde(with = "humantime_serde")]
71 pub exec_timeout: Duration,
72 #[serde(with = "humantime_serde")]
74 pub boot_timeout: Duration,
75}
76
77impl Default for WorkersConfig {
78 fn default() -> Self {
79 Self {
80 script: "vendor/bin/folk-worker".into(),
81 php: "php".into(),
82 count: 4,
83 max_jobs: 1000,
84 ttl: Duration::from_secs(3600),
85 max_memory_mb: 256,
86 exec_timeout: Duration::from_secs(30),
87 boot_timeout: Duration::from_secs(30),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93#[serde(default)]
94pub struct LogConfig {
95 pub filter: String,
97 pub format: LogFormat,
99}
100
101impl Default for LogConfig {
102 fn default() -> Self {
103 Self {
104 filter: "info".into(),
105 format: LogFormat::Text,
106 }
107 }
108}
109
110#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
111#[serde(rename_all = "snake_case")]
112pub enum LogFormat {
113 Text,
114 Json,
115 Pretty,
116}
117
118impl FolkConfig {
119 pub fn load() -> Result<Self> {
122 Self::load_from(Path::new("folk.toml"))
123 }
124
125 pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
127 let path = path.as_ref();
128 let mut fig = Figment::from(figment::providers::Serialized::defaults(Self::default()));
129 if path.exists() {
130 fig = fig.merge(Toml::file(path));
131 }
132 fig = fig.merge(Env::prefixed("FOLK_").split("_"));
133 fig.extract().context("failed to parse Folk configuration")
134 }
135}