1use std::net::{Ipv4Addr, SocketAddr};
2
3use url::Url;
4use vector_lib::configurable::configurable_component;
5
6#[configurable_component]
8#[configurable(metadata(
9 docs::warnings = "The API currently does not support authentication. Only enable it in isolated environments or for debugging. It must not be exposed to untrusted clients."
10))]
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12#[serde(default, deny_unknown_fields)]
13pub struct Options {
14 #[serde(default = "default_enabled")]
16 #[configurable(metadata(docs::common = true, docs::required = false))]
17 pub enabled: bool,
18
19 #[serde(default = "default_address")]
23 #[configurable(metadata(docs::examples = "0.0.0.0:8686"))]
24 #[configurable(metadata(docs::examples = "127.0.0.1:1234"))]
25 #[configurable(metadata(docs::common = true, docs::required = false))]
26 pub address: Option<SocketAddr>,
27
28 #[serde(default = "default_playground")]
33 #[configurable(metadata(docs::common = false, docs::required = false))]
34 pub playground: bool,
35
36 #[serde(default = "default_graphql", skip_serializing_if = "is_true")]
40 #[configurable(metadata(docs::common = true, docs::required = false))]
41 pub graphql: bool,
42}
43
44impl_generate_config_from_default!(Options);
45
46impl Default for Options {
47 fn default() -> Self {
48 Self {
49 enabled: default_enabled(),
50 playground: default_playground(),
51 address: default_address(),
52 graphql: default_graphql(),
53 }
54 }
55}
56
57#[allow(clippy::trivially_copy_pass_by_ref)]
59const fn is_true(value: &bool) -> bool {
60 *value
61}
62
63const fn default_enabled() -> bool {
64 false
65}
66
67pub fn default_address() -> Option<SocketAddr> {
71 Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 8686))
72}
73
74pub fn default_graphql_url() -> Url {
76 let addr = default_address().unwrap();
77 Url::parse(&format!("http://{addr}/graphql"))
78 .expect("Couldn't parse default API URL. Please report this.")
79}
80
81const fn default_playground() -> bool {
82 true
83}
84
85const fn default_graphql() -> bool {
86 true
87}
88
89impl Options {
90 pub fn merge(&mut self, other: Self) -> Result<(), String> {
91 let address = match (self.address, other.address) {
95 (None, b) => b,
96 (Some(a), None) => Some(a),
97 (Some(a), Some(b)) if a == b => Some(a),
98 (Some(a), Some(b)) => {
100 match (Some(a) == default_address(), Some(b) == default_address()) {
101 (false, false) => return Err(format!("Conflicting `api` address: {a}, {b} .")),
102 (false, true) => Some(a),
103 (true, _) => Some(b),
104 }
105 }
106 };
107
108 let options = Options {
109 address,
110 enabled: self.enabled | other.enabled,
111 playground: self.playground & other.playground,
112 graphql: self.graphql & other.graphql,
113 };
114
115 *self = options;
116 Ok(())
117 }
118}
119
120#[test]
121fn bool_merge() {
122 let mut a = Options {
123 enabled: true,
124 address: None,
125 playground: false,
126 graphql: false,
127 };
128
129 a.merge(Options::default()).unwrap();
130
131 assert_eq!(
132 a,
133 Options {
134 enabled: true,
135 address: default_address(),
136 playground: false,
137 graphql: false
138 }
139 );
140}
141
142#[test]
143fn bind_merge() {
144 let address = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000);
145 let mut a = Options {
146 enabled: true,
147 address: Some(address),
148 playground: true,
149 graphql: true,
150 };
151
152 a.merge(Options::default()).unwrap();
153
154 assert_eq!(
155 a,
156 Options {
157 enabled: true,
158 address: Some(address),
159 playground: true,
160 graphql: true,
161 }
162 );
163}
164
165#[test]
166fn bind_conflict() {
167 let mut a = Options {
168 address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9000)),
169 ..Options::default()
170 };
171
172 let b = Options {
173 address: Some(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9001)),
174 ..Options::default()
175 };
176
177 assert!(a.merge(b).is_err());
178}