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