zone_edit/
lib.rs

1mod http;
2pub mod errors;
3
4
5#[cfg(feature = "dnsimple")]
6pub mod dnsimple;
7#[cfg(feature = "gandi")]
8pub mod gandi;
9
10use std::{fmt::{self, Debug, Display, Formatter}, net::Ipv4Addr};
11
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13use tracing::warn;
14
15use crate::errors::Result;
16
17
18pub struct Config {
19    pub domain: String,
20    pub dry_run: bool,
21}
22
23#[derive(Serialize, Deserialize, Clone, Debug)]
24pub enum RecordType {
25    A,
26    AAAA,
27    CAA,
28    CNAME,
29    HINFO,
30    MX,
31    NAPTR,
32    NS,
33    PTR,
34    SRV,
35    SPF,
36    SSHFP,
37    TXT,
38}
39
40impl Display for RecordType {
41    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
42        write!(f, "{:?}", self)
43    }
44}
45
46#[allow(unused)]
47pub trait DnsProvider {
48    fn get_record<T>(&self, rtype: RecordType, host: &str) -> impl Future<Output = Result<Option<T>>>
49    where
50        T: DeserializeOwned;
51
52    fn create_record<T>(&self, rtype: RecordType, host: &str, record: &T) -> impl Future<Output = Result<()>>
53    where
54        T: Serialize + DeserializeOwned + Display + Clone + Send + Sync;
55
56    fn update_record<T>(&self, rtype: RecordType, host: &str, record: &T) -> impl Future<Output = Result<()>>
57    where
58        T: Serialize + DeserializeOwned + Display + Clone + Send + Sync;
59
60    fn delete_record(&self, rtype: RecordType, host: &str) -> impl Future<Output = Result<()>>;
61
62
63    // Default helper impls
64
65    // We know all the types, and they're enforced above, so this lint
66    // doesn't apply here(?)
67    #[allow(async_fn_in_trait)]
68    async fn get_txt_record(&self, host: &str) -> Result<Option<String>> {
69        self.get_record::<String>(RecordType::TXT, host).await
70            .map(|opt| opt.map(|s| strip_quotes(&s)))
71    }
72
73    #[allow(async_fn_in_trait)]
74    async fn create_txt_record(&self, host: &str, record: &String) -> Result<()> {
75        self.create_record(RecordType::TXT, host, record).await
76    }
77
78    #[allow(async_fn_in_trait)]
79    async fn update_txt_record(&self, host: &str, record: &String) -> Result<()> {
80        self.update_record(RecordType::TXT, host, record).await
81    }
82
83    #[allow(async_fn_in_trait)]
84    async fn delete_txt_record(&self, host: &str) -> Result<()> {
85        self.delete_record(RecordType::TXT, host).await
86    }
87
88    #[allow(async_fn_in_trait)]
89    async fn get_a_record(&self, host: &str) -> Result<Option<Ipv4Addr>> {
90        self.get_record(RecordType::A, host).await
91    }
92
93    #[allow(async_fn_in_trait)]
94    async fn create_a_record(&self, host: &str, record: &Ipv4Addr) -> Result<()> {
95        self.create_record(RecordType::A, host, record).await
96    }
97
98    #[allow(async_fn_in_trait)]
99    async fn update_a_record(&self, host: &str, record: &Ipv4Addr) -> Result<()> {
100        self.update_record(RecordType::A, host, record).await
101    }
102
103    #[allow(async_fn_in_trait)]
104    async fn delete_a_record(&self, host: &str) -> Result<()> {
105        self.delete_record(RecordType::A, host).await
106    }
107}
108
109
110fn strip_quotes(record: &str) -> String {
111    let chars = record.chars();
112    let mut check = chars.clone();
113
114    let first = check.nth(0);
115    let last = check.last();
116
117    if let Some('"') = first && let Some('"') = last {
118        let stripped = chars.skip(1)
119            .take(record.len() - 2)
120            .collect();
121        stripped
122    } else {
123        warn!("Double quotes not found in record string, using whole record.");
124        record.to_string()
125    }
126}
127
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_strip_quotes() -> Result<()> {
135        assert_eq!("abc123".to_string(), strip_quotes("\"abc123\""));
136        assert_eq!("abc123\"", strip_quotes("abc123\""));
137        assert_eq!("\"abc123", strip_quotes("\"abc123"));
138        assert_eq!("abc123", strip_quotes("abc123"));
139
140        Ok(())
141    }
142
143}