use std::path::Path;
use std::time::Duration;
use anyhow::{Context, Result};
use figment::Figment;
use figment::providers::{Env, Format, Toml};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct FolkConfig {
pub server: ServerConfig,
pub workers: WorkersConfig,
pub log: LogConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
pub rpc_socket: String,
pub runtime: RuntimeKind,
#[serde(with = "humantime_serde")]
pub shutdown_timeout: Duration,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
rpc_socket: "/tmp/folk.sock".into(),
runtime: RuntimeKind::Pipe,
shutdown_timeout: Duration::from_secs(30),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeKind {
Pipe,
Fork,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct WorkersConfig {
pub script: String,
pub php: String,
pub count: usize,
pub max_jobs: u64,
#[serde(with = "humantime_serde")]
pub ttl: Duration,
pub max_memory_mb: u64,
#[serde(with = "humantime_serde")]
pub exec_timeout: Duration,
#[serde(with = "humantime_serde")]
pub boot_timeout: Duration,
}
impl Default for WorkersConfig {
fn default() -> Self {
Self {
script: "vendor/bin/folk-worker".into(),
php: "php".into(),
count: 4,
max_jobs: 1000,
ttl: Duration::from_secs(3600),
max_memory_mb: 256,
exec_timeout: Duration::from_secs(30),
boot_timeout: Duration::from_secs(30),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct LogConfig {
pub filter: String,
pub format: LogFormat,
}
impl Default for LogConfig {
fn default() -> Self {
Self {
filter: "info".into(),
format: LogFormat::Text,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum LogFormat {
Text,
Json,
Pretty,
}
impl FolkConfig {
pub fn load() -> Result<Self> {
Self::load_from(Path::new("folk.toml"))
}
pub fn load_from(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let mut fig = Figment::from(figment::providers::Serialized::defaults(Self::default()));
if path.exists() {
fig = fig.merge(Toml::file(path));
}
fig = fig.merge(Env::prefixed("FOLK_").split("_"));
fig.extract().context("failed to parse Folk configuration")
}
}