The EngineConfig struct defines the structure of the config.yaml file used to configure the iii framework engine.
EngineConfig Struct
#[derive(Debug, Deserialize)]
pub struct EngineConfig {
#[serde(default = "default_port")]
pub port: u16,
#[serde(default)]
pub modules: Vec<ModuleEntry>,
}
Fields
WebSocket server port. Defaults to 49134 if not specified.
modules
Vec<ModuleEntry>
default:"[]"
List of modules to load. Each entry specifies a module class and optional configuration.
ModuleEntry Struct
#[derive(Debug, Deserialize)]
pub struct ModuleEntry {
pub class: String,
#[serde(default)]
pub config: Option<Value>,
}
Fields
Fully qualified module class name (e.g., "modules::api::RestApiModule")
config
Option<Value>
default:"None"
Module-specific JSON configuration. Structure varies by module type.
YAML Configuration
Basic Example
port: 49134
modules:
- class: modules::api::RestApiModule
config:
port: 3111
host: 127.0.0.1
- class: modules::queue::QueueModule
config:
adapter:
class: modules::queue::RedisAdapter
config:
redis_url: redis://localhost:6379
With Environment Variables
The configuration supports environment variable expansion using the ${VAR_NAME} or ${VAR_NAME:default} syntax:
port: ${PORT:49134}
modules:
- class: modules::queue::QueueModule
config:
adapter:
class: modules::queue::RedisAdapter
config:
redis_url: ${REDIS_URL:redis://localhost:6379}
- class: modules::observability::OtelModule
config:
endpoint: ${OTEL_ENDPOINT:http://localhost:4317}
service_name: ${SERVICE_NAME:iii-engine}
Environment Variable Syntax:
${VAR_NAME} - Required variable (panics if not set)
${VAR_NAME:default} - Optional with default value
${VAR_NAME:} - Optional with empty string default
Complete Example
port: 49134
modules:
# HTTP API Module
- class: modules::api::RestApiModule
config:
port: 3111
host: 127.0.0.1
# Queue Module with Redis
- class: modules::queue::QueueModule
config:
adapter:
class: modules::queue::RedisAdapter
config:
redis_url: ${REDIS_URL:redis://localhost:6379}
# Stream Module with Redis
- class: modules::stream::StreamModule
config:
adapter:
class: modules::stream::adapters::RedisAdapter
config:
redis_url: ${REDIS_URL:redis://localhost:6379}
# Cron Module
- class: modules::cron::CronModule
# State Module with File Storage
- class: modules::state::StateModule
config:
adapter:
class: modules::state::adapters::FileAdapter
config:
path: ./data/state
# Observability Module
- class: modules::observability::OtelModule
config:
endpoint: ${OTEL_ENDPOINT:http://localhost:4317}
service_name: iii-engine
Methods
EngineConfig::config_file_or_default()
Loads configuration from a file or returns default configuration.
pub fn config_file_or_default(path: &str) -> anyhow::Result<Self>
Path to YAML configuration file
Returns: Result<EngineConfig> - Parsed config or error
Behavior:
- If file exists: Parses YAML, expands environment variables, returns config
- If file missing: Returns config with default modules from inventory
- If parse error: Returns error with details
Example:
use iii::modules::config::EngineConfig;
let config = EngineConfig::config_file_or_default("config.yaml")?;
let port = if config.port == 0 {
DEFAULT_PORT
} else {
config.port
};
EngineConfig::expand_env_vars()
Expands environment variables in YAML content.
pub(crate) fn expand_env_vars(yaml_content: &str) -> String
Raw YAML content containing environment variable placeholders
Returns: String - YAML content with variables expanded
Panics: If a required variable (without default) is not set
Example:
use std::env;
env::set_var("TEST_HOST", "localhost");
env::set_var("TEST_PORT", "8080");
let input = r#"server:
host: ${TEST_HOST}
port: ${TEST_PORT}
timeout: ${TEST_TIMEOUT:30}"#;
let output = EngineConfig::expand_env_vars(input);
// Output:
// server:
// host: localhost
// port: 8080
// timeout: 30
EngineConfig::default_modules()
Returns configuration with default modules from the inventory.
pub fn default_modules(self) -> Self
Returns: EngineConfig with default port and modules
Constants
DEFAULT_PORT
pub const DEFAULT_PORT: u16 = 49134;
Default WebSocket server port used when not specified in config.
DEFAULT_HOST
const DEFAULT_HOST: &str = "0.0.0.0";
Default server host (binds to all interfaces).
Module Configuration by Type
HTTP Module
- class: modules::api::RestApiModule
config:
port: 3111 # HTTP server port
host: 127.0.0.1 # Bind address
Queue Module
- class: modules::queue::QueueModule
config:
adapter:
class: modules::queue::RedisAdapter
config:
redis_url: redis://localhost:6379
Stream Module
- class: modules::stream::StreamModule
config:
adapter:
class: modules::stream::adapters::RedisAdapter
config:
redis_url: redis://localhost:6379
State Module
- class: modules::state::StateModule
config:
adapter:
class: modules::state::adapters::FileAdapter
config:
path: ./data/state
Cron Module
- class: modules::cron::CronModule
# No configuration required
Observability Module
- class: modules::observability::OtelModule
config:
endpoint: http://localhost:4317
service_name: my-service
Default Modules
When using config_file_or_default() with a missing config file, the following default modules are loaded:
- All modules registered in the inventory with
is_default = true
- Modules are loaded with
config: None
You can check which modules are registered by examining the codebase for inventory::submit! macro calls.
Environment Variable Examples
Required Variables
modules:
- class: modules::queue::QueueModule
config:
adapter:
config:
redis_url: ${REDIS_URL} # Must be set or panics
Optional with Defaults
port: ${PORT:49134}
modules:
- class: modules::observability::OtelModule
config:
endpoint: ${OTEL_ENDPOINT:http://localhost:4317}
service_name: ${SERVICE_NAME:iii-engine}
Complex URLs
modules:
- class: modules::queue::QueueModule
config:
adapter:
config:
# Colons in default value work correctly
redis_url: ${REDIS_URL:redis://localhost:6379/0}
Loading Configuration
From File
use iii::EngineBuilder;
EngineBuilder::new()
.config_file_or_default("config.yaml")? // Loads from file or uses defaults
.build()
.await?
Programmatically
use iii::EngineBuilder;
use serde_json::json;
EngineBuilder::new()
.address("0.0.0.0:8080")
.add_module("modules::api::RestApiModule", Some(json!({
"port": 3111,
"host": "127.0.0.1"
})))
.build()
.await?
Error Handling
Parse Errors
match EngineConfig::config_file_or_default("config.yaml") {
Ok(config) => {
// Config loaded successfully
}
Err(e) => {
eprintln!("Failed to load config: {}", e);
// Error message includes file path and parse details
}
}
Missing Environment Variables
# This will panic if REQUIRED_VAR is not set
modules:
- class: modules::custom::Module
config:
api_key: ${REQUIRED_VAR}
# Set before running
export REQUIRED_VAR=my-secret-key
./engine -c config.yaml
Best Practices
Use environment variables for secrets: Never commit API keys, passwords, or tokens to config files
Provide sensible defaults: Use ${VAR:default} syntax for non-sensitive configuration
Document required variables: List all required environment variables in your README
Environment variables without defaults will panic if not set. Use defaults for optional configuration.
EngineBuilder
Learn about the builder API
Modules Overview
Available modules and configuration
Deployment
Production configuration guide
Custom Modules
Build custom modules