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 Embed,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(default)]
56pub struct WorkersConfig {
57 pub script: String,
59 pub php: String,
61 pub count: usize,
63 pub max_jobs: u64,
65 #[serde(with = "humantime_serde")]
67 pub ttl: Duration,
68 pub max_memory_mb: u64,
70 #[serde(with = "humantime_serde")]
72 pub exec_timeout: Duration,
73 #[serde(with = "humantime_serde")]
75 pub boot_timeout: Duration,
76}
77
78impl Default for WorkersConfig {
79 fn default() -> Self {
80 Self {
81 script: "vendor/bin/folk-worker".into(),
82 php: "php".into(),
83 count: 4,
84 max_jobs: 1000,
85 ttl: Duration::from_secs(3600),
86 max_memory_mb: 256,
87 exec_timeout: Duration::from_secs(30),
88 boot_timeout: Duration::from_secs(30),
89 }
90 }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94#[serde(default)]
95pub struct LogConfig {
96 pub filter: String,
98 pub format: LogFormat,
100}
101
102impl Default for LogConfig {
103 fn default() -> Self {
104 Self {
105 filter: "info".into(),
106 format: LogFormat::Text,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
112#[serde(rename_all = "snake_case")]
113pub enum LogFormat {
114 Text,
115 Json,
116 Pretty,
117}
118
119impl FolkConfig {
120 pub fn load() -> Result<Self> {
123 Self::load_from(Path::new("folk.toml"))
124 }
125
126 pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
128 let path = path.as_ref();
129 let mut fig = Figment::from(figment::providers::Serialized::defaults(Self::default()));
130 if path.exists() {
131 fig = fig.merge(Toml::file(path));
132 }
133 fig = fig.merge(Env::prefixed("FOLK_").split("_"));
134 fig.extract().context("failed to parse Folk configuration")
135 }
136}