runtara_sdk/
config.rs

1// Copyright (C) 2025 SyncMyOrders Sp. z o.o.
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! SDK configuration for connecting to runtara-core.
4
5use std::env;
6use std::net::SocketAddr;
7
8use crate::error::{Result, SdkError};
9
10/// SDK configuration for connecting to runtara-core.
11#[derive(Debug, Clone)]
12pub struct SdkConfig {
13    /// Instance ID (required) - unique identifier for this instance
14    pub instance_id: String,
15    /// Tenant ID (required) - tenant this instance belongs to
16    pub tenant_id: String,
17    /// Server address (default: "127.0.0.1:8001")
18    pub server_addr: SocketAddr,
19    /// Server name for TLS verification (default: "localhost")
20    pub server_name: String,
21    /// Skip TLS certificate verification (default: false, use true for dev)
22    pub skip_cert_verification: bool,
23    /// Connection timeout in milliseconds (default: 10_000)
24    pub connect_timeout_ms: u64,
25    /// Request timeout in milliseconds (default: 30_000)
26    pub request_timeout_ms: u64,
27    /// Signal poll interval in milliseconds (default: 1_000)
28    pub signal_poll_interval_ms: u64,
29}
30
31impl SdkConfig {
32    /// Load configuration from environment variables.
33    ///
34    /// # Required Environment Variables
35    /// - `RUNTARA_INSTANCE_ID` - Unique identifier for this instance
36    /// - `RUNTARA_TENANT_ID` - Tenant this instance belongs to
37    ///
38    /// # Optional Environment Variables
39    /// - `RUNTARA_SERVER_ADDR` - Server address (default: "127.0.0.1:8001")
40    /// - `RUNTARA_SERVER_NAME` - Server name for TLS (default: "localhost")
41    /// - `RUNTARA_SKIP_CERT_VERIFICATION` - Skip TLS verification (default: false)
42    /// - `RUNTARA_CONNECT_TIMEOUT_MS` - Connection timeout (default: 10000)
43    /// - `RUNTARA_REQUEST_TIMEOUT_MS` - Request timeout (default: 30000)
44    /// - `RUNTARA_SIGNAL_POLL_INTERVAL_MS` - Signal poll interval (default: 1000)
45    pub fn from_env() -> Result<Self> {
46        let instance_id = env::var("RUNTARA_INSTANCE_ID")
47            .map_err(|_| SdkError::Config("RUNTARA_INSTANCE_ID is required".to_string()))?;
48
49        let tenant_id = env::var("RUNTARA_TENANT_ID")
50            .map_err(|_| SdkError::Config("RUNTARA_TENANT_ID is required".to_string()))?;
51
52        let server_addr = env::var("RUNTARA_SERVER_ADDR")
53            .unwrap_or_else(|_| "127.0.0.1:8001".to_string())
54            .parse()
55            .map_err(|e| SdkError::Config(format!("invalid RUNTARA_SERVER_ADDR: {}", e)))?;
56
57        let server_name =
58            env::var("RUNTARA_SERVER_NAME").unwrap_or_else(|_| "localhost".to_string());
59
60        let skip_cert_verification = env::var("RUNTARA_SKIP_CERT_VERIFICATION")
61            .map(|v| v == "true" || v == "1")
62            .unwrap_or(false);
63
64        let connect_timeout_ms = env::var("RUNTARA_CONNECT_TIMEOUT_MS")
65            .ok()
66            .and_then(|v| v.parse().ok())
67            .unwrap_or(10_000);
68
69        let request_timeout_ms = env::var("RUNTARA_REQUEST_TIMEOUT_MS")
70            .ok()
71            .and_then(|v| v.parse().ok())
72            .unwrap_or(30_000);
73
74        let signal_poll_interval_ms = env::var("RUNTARA_SIGNAL_POLL_INTERVAL_MS")
75            .ok()
76            .and_then(|v| v.parse().ok())
77            .unwrap_or(1_000);
78
79        Ok(Self {
80            instance_id,
81            tenant_id,
82            server_addr,
83            server_name,
84            skip_cert_verification,
85            connect_timeout_ms,
86            request_timeout_ms,
87            signal_poll_interval_ms,
88        })
89    }
90
91    /// Create a configuration for local development.
92    ///
93    /// This sets up reasonable defaults for local development:
94    /// - Connects to `127.0.0.1:8001`
95    /// - Skips TLS certificate verification
96    pub fn localhost(instance_id: impl Into<String>, tenant_id: impl Into<String>) -> Self {
97        Self {
98            instance_id: instance_id.into(),
99            tenant_id: tenant_id.into(),
100            server_addr: "127.0.0.1:8001".parse().unwrap(),
101            server_name: "localhost".to_string(),
102            skip_cert_verification: true,
103            connect_timeout_ms: 10_000,
104            request_timeout_ms: 30_000,
105            signal_poll_interval_ms: 1_000,
106        }
107    }
108
109    /// Create a new configuration with the given instance and tenant IDs.
110    pub fn new(instance_id: impl Into<String>, tenant_id: impl Into<String>) -> Self {
111        Self {
112            instance_id: instance_id.into(),
113            tenant_id: tenant_id.into(),
114            server_addr: "127.0.0.1:8001".parse().unwrap(),
115            server_name: "localhost".to_string(),
116            skip_cert_verification: false,
117            connect_timeout_ms: 10_000,
118            request_timeout_ms: 30_000,
119            signal_poll_interval_ms: 1_000,
120        }
121    }
122
123    /// Set the server address.
124    pub fn with_server_addr(mut self, addr: SocketAddr) -> Self {
125        self.server_addr = addr;
126        self
127    }
128
129    /// Set the server name for TLS verification.
130    pub fn with_server_name(mut self, name: impl Into<String>) -> Self {
131        self.server_name = name.into();
132        self
133    }
134
135    /// Skip TLS certificate verification (for development only!).
136    pub fn with_skip_cert_verification(mut self, skip: bool) -> Self {
137        self.skip_cert_verification = skip;
138        self
139    }
140
141    /// Set the signal poll interval.
142    pub fn with_signal_poll_interval_ms(mut self, interval_ms: u64) -> Self {
143        self.signal_poll_interval_ms = interval_ms;
144        self
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn test_localhost_config() {
154        let config = SdkConfig::localhost("test-instance", "test-tenant");
155        assert_eq!(config.instance_id, "test-instance");
156        assert_eq!(config.tenant_id, "test-tenant");
157        assert!(config.skip_cert_verification);
158        assert_eq!(config.server_addr, "127.0.0.1:8001".parse().unwrap());
159    }
160
161    #[test]
162    fn test_builder_pattern() {
163        let config = SdkConfig::new("inst", "tenant")
164            .with_server_addr("192.168.1.1:8000".parse().unwrap())
165            .with_skip_cert_verification(true)
166            .with_signal_poll_interval_ms(500);
167
168        assert_eq!(config.server_addr, "192.168.1.1:8000".parse().unwrap());
169        assert!(config.skip_cert_verification);
170        assert_eq!(config.signal_poll_interval_ms, 500);
171    }
172}