bindcar/
rndc_conf_types.rs1use std::collections::HashMap;
28use std::net::IpAddr;
29use std::path::PathBuf;
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct RndcConfFile {
34 pub keys: HashMap<String, KeyBlock>,
36
37 pub servers: HashMap<String, ServerBlock>,
39
40 pub options: OptionsBlock,
42
43 pub includes: Vec<PathBuf>,
45}
46
47impl RndcConfFile {
48 pub fn new() -> Self {
50 Self {
51 keys: HashMap::new(),
52 servers: HashMap::new(),
53 options: OptionsBlock::default(),
54 includes: Vec::new(),
55 }
56 }
57
58 pub fn get_default_key(&self) -> Option<&KeyBlock> {
60 let key_name = self.options.default_key.as_ref()?;
61 self.keys.get(key_name)
62 }
63
64 pub fn get_default_server(&self) -> Option<String> {
66 self.options.default_server.clone()
67 }
68
69 pub fn to_conf_file(&self) -> String {
71 let mut output = String::new();
72
73 for include_path in &self.includes {
75 output.push_str(&format!("include \"{}\";\n", include_path.display()));
76 }
77
78 for (name, key) in &self.keys {
80 output.push_str(&format!("\nkey \"{}\" {}\n", name, key.to_conf_block()));
81 }
82
83 for (addr, server) in &self.servers {
85 output.push_str(&format!("\nserver {} {}\n", addr, server.to_conf_block()));
86 }
87
88 if !self.options.is_empty() {
90 output.push_str(&format!("\noptions {}\n", self.options.to_conf_block()));
91 }
92
93 output
94 }
95}
96
97impl Default for RndcConfFile {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct KeyBlock {
106 pub name: String,
107 pub algorithm: String,
108 pub secret: String,
109}
110
111impl KeyBlock {
112 pub fn new(name: String, algorithm: String, secret: String) -> Self {
114 Self {
115 name,
116 algorithm,
117 secret,
118 }
119 }
120
121 pub fn to_conf_block(&self) -> String {
131 format!(
132 "{{\n algorithm {};\n secret \"{}\";\n}};",
133 self.algorithm, self.secret
134 )
135 }
136}
137
138#[derive(Debug, Clone, PartialEq)]
140pub struct ServerBlock {
141 pub address: ServerAddress,
142 pub key: Option<String>,
143 pub port: Option<u16>,
144 pub addresses: Option<Vec<IpAddr>>,
145}
146
147impl ServerBlock {
148 pub fn new(address: ServerAddress) -> Self {
150 Self {
151 address,
152 key: None,
153 port: None,
154 addresses: None,
155 }
156 }
157
158 pub fn to_conf_block(&self) -> String {
168 let mut parts = Vec::new();
169
170 if let Some(ref key) = self.key {
171 parts.push(format!(" key \"{}\";", key));
172 }
173
174 if let Some(port) = self.port {
175 parts.push(format!(" port {};", port));
176 }
177
178 if let Some(ref addrs) = self.addresses {
179 let addr_list = addrs
180 .iter()
181 .map(|ip| format!(" {};", ip))
182 .collect::<Vec<_>>()
183 .join("\n");
184 parts.push(format!(" addresses {{\n{}\n }};", addr_list));
185 }
186
187 if parts.is_empty() {
188 "{ };".to_string()
189 } else {
190 format!("{{\n{}\n}};", parts.join("\n"))
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Eq)]
197pub enum ServerAddress {
198 Hostname(String),
199 IpAddr(IpAddr),
200}
201
202impl ServerAddress {
203 pub fn parse(s: &str) -> Self {
205 match s.parse::<IpAddr>() {
206 Ok(addr) => ServerAddress::IpAddr(addr),
207 Err(_) => ServerAddress::Hostname(s.to_string()),
208 }
209 }
210}
211
212impl std::fmt::Display for ServerAddress {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 match self {
215 ServerAddress::Hostname(h) => write!(f, "{}", h),
216 ServerAddress::IpAddr(ip) => write!(f, "{}", ip),
217 }
218 }
219}
220
221#[derive(Debug, Clone, Default, PartialEq, Eq)]
223pub struct OptionsBlock {
224 pub default_server: Option<String>,
225 pub default_key: Option<String>,
226 pub default_port: Option<u16>,
227}
228
229impl OptionsBlock {
230 pub fn new() -> Self {
232 Self::default()
233 }
234
235 pub fn is_empty(&self) -> bool {
237 self.default_server.is_none() && self.default_key.is_none() && self.default_port.is_none()
238 }
239
240 pub fn to_conf_block(&self) -> String {
251 let mut parts = Vec::new();
252
253 if let Some(ref server) = self.default_server {
254 parts.push(format!(" default-server {};", server));
255 }
256
257 if let Some(ref key) = self.default_key {
258 parts.push(format!(" default-key \"{}\";", key));
259 }
260
261 if let Some(port) = self.default_port {
262 parts.push(format!(" default-port {};", port));
263 }
264
265 if parts.is_empty() {
266 "{ };".to_string()
267 } else {
268 format!("{{\n{}\n}};", parts.join("\n"))
269 }
270 }
271}
272
273#[cfg(test)]
274#[path = "rndc_conf_types_tests.rs"]
275mod tests;