Skip to main content

acp_runtime/
options.rs

1// Copyright 2026 ACP Project
2// Licensed under the Apache License, Version 2.0
3// See LICENSE file for details.
4
5use std::collections::HashMap;
6use std::path::PathBuf;
7use std::sync::Arc;
8
9use serde_json::{Map, Value};
10
11use crate::amqp_transport::{
12    AmqpTransportClient, DEFAULT_AMQP_EXCHANGE, DEFAULT_AMQP_EXCHANGE_TYPE,
13};
14use crate::capabilities::AgentCapabilities;
15use crate::key_provider::KeyProvider;
16use crate::messages::DeliveryMode;
17use crate::mqtt_transport::{DEFAULT_MQTT_QOS, DEFAULT_MQTT_TOPIC_PREFIX, MqttTransportClient};
18
19#[derive(Clone)]
20pub struct AcpAgentOptions {
21    pub storage_dir: PathBuf,
22    pub endpoint: Option<String>,
23    pub relay_url: String,
24    pub relay_hints: Vec<String>,
25    pub enterprise_directory_hints: Vec<String>,
26    pub discovery_scheme: String,
27    pub trust_profile: String,
28    pub capabilities: Option<AgentCapabilities>,
29    pub default_delivery_mode: DeliveryMode,
30    pub http_timeout_seconds: u64,
31    pub allow_insecure_http: bool,
32    pub allow_insecure_tls: bool,
33    pub mtls_enabled: bool,
34    pub ca_file: Option<String>,
35    pub cert_file: Option<String>,
36    pub key_file: Option<String>,
37    pub key_provider: String,
38    pub vault_url: Option<String>,
39    pub vault_path: Option<String>,
40    pub vault_token_env: String,
41    pub vault_token: Option<String>,
42    pub key_provider_instance: Option<Arc<dyn KeyProvider>>,
43    pub amqp_broker_url: Option<String>,
44    pub amqp_exchange: String,
45    pub amqp_exchange_type: String,
46    pub amqp_transport: Option<AmqpTransportClient>,
47    pub mqtt_broker_url: Option<String>,
48    pub mqtt_qos: u8,
49    pub mqtt_topic_prefix: String,
50    pub mqtt_transport: Option<MqttTransportClient>,
51    pub extra: HashMap<String, Value>,
52}
53
54impl Default for AcpAgentOptions {
55    fn default() -> Self {
56        Self {
57            storage_dir: PathBuf::from(".acp-data"),
58            endpoint: None,
59            relay_url: "https://localhost:8080".to_string(),
60            relay_hints: Vec::new(),
61            enterprise_directory_hints: Vec::new(),
62            discovery_scheme: "https".to_string(),
63            trust_profile: "self_asserted".to_string(),
64            capabilities: None,
65            default_delivery_mode: DeliveryMode::Auto,
66            http_timeout_seconds: 10,
67            allow_insecure_http: false,
68            allow_insecure_tls: false,
69            mtls_enabled: false,
70            ca_file: None,
71            cert_file: None,
72            key_file: None,
73            key_provider: "local".to_string(),
74            vault_url: None,
75            vault_path: None,
76            vault_token_env: "VAULT_TOKEN".to_string(),
77            vault_token: None,
78            key_provider_instance: None,
79            amqp_broker_url: None,
80            amqp_exchange: DEFAULT_AMQP_EXCHANGE.to_string(),
81            amqp_exchange_type: DEFAULT_AMQP_EXCHANGE_TYPE.to_string(),
82            amqp_transport: None,
83            mqtt_broker_url: None,
84            mqtt_qos: DEFAULT_MQTT_QOS,
85            mqtt_topic_prefix: DEFAULT_MQTT_TOPIC_PREFIX.to_string(),
86            mqtt_transport: None,
87            extra: HashMap::new(),
88        }
89    }
90}
91
92impl AcpAgentOptions {
93    pub fn to_config_map(&self) -> Map<String, Value> {
94        let mut values = Map::new();
95        values.insert(
96            "allow_insecure_http".to_string(),
97            Value::Bool(self.allow_insecure_http),
98        );
99        values.insert(
100            "allow_insecure_tls".to_string(),
101            Value::Bool(self.allow_insecure_tls),
102        );
103        values.insert("mtls_enabled".to_string(), Value::Bool(self.mtls_enabled));
104        values.insert(
105            "ca_file".to_string(),
106            self.ca_file
107                .clone()
108                .map(Value::String)
109                .unwrap_or(Value::Null),
110        );
111        values.insert(
112            "cert_file".to_string(),
113            self.cert_file
114                .clone()
115                .map(Value::String)
116                .unwrap_or(Value::Null),
117        );
118        values.insert(
119            "key_file".to_string(),
120            self.key_file
121                .clone()
122                .map(Value::String)
123                .unwrap_or(Value::Null),
124        );
125        values.insert(
126            "key_provider".to_string(),
127            Value::String(self.key_provider.clone()),
128        );
129        values.insert(
130            "vault_url".to_string(),
131            self.vault_url
132                .clone()
133                .map(Value::String)
134                .unwrap_or(Value::Null),
135        );
136        values.insert(
137            "vault_path".to_string(),
138            self.vault_path
139                .clone()
140                .map(Value::String)
141                .unwrap_or(Value::Null),
142        );
143        values.insert(
144            "vault_token_env".to_string(),
145            Value::String(self.vault_token_env.clone()),
146        );
147        values
148    }
149
150    pub fn from_config_map(config: Option<&Map<String, Value>>) -> Self {
151        let mut options = Self::default();
152        let Some(config) = config else {
153            return options;
154        };
155        options.allow_insecure_http = as_bool(config.get("allow_insecure_http"), false);
156        options.allow_insecure_tls = as_bool(config.get("allow_insecure_tls"), false);
157        options.mtls_enabled = as_bool(config.get("mtls_enabled"), false);
158        options.ca_file = as_string(config.get("ca_file"));
159        options.cert_file = as_string(config.get("cert_file"));
160        options.key_file = as_string(config.get("key_file"));
161        options.key_provider =
162            as_string(config.get("key_provider")).unwrap_or_else(|| "local".to_string());
163        options.vault_url = as_string(config.get("vault_url"));
164        options.vault_path = as_string(config.get("vault_path"));
165        options.vault_token_env =
166            as_string(config.get("vault_token_env")).unwrap_or_else(|| "VAULT_TOKEN".to_string());
167        options
168    }
169}
170
171fn as_bool(value: Option<&Value>, default_value: bool) -> bool {
172    match value {
173        Some(Value::Bool(v)) => *v,
174        Some(Value::String(v)) => match v.trim().to_lowercase().as_str() {
175            "1" | "true" | "yes" | "on" => true,
176            "0" | "false" | "no" | "off" => false,
177            _ => default_value,
178        },
179        _ => default_value,
180    }
181}
182
183fn as_string(value: Option<&Value>) -> Option<String> {
184    value
185        .and_then(Value::as_str)
186        .map(str::trim)
187        .filter(|v| !v.is_empty())
188        .map(str::to_string)
189}