Skip to main content

simply_dns/
client.rs

1use reqwest::Client;
2use thiserror::Error;
3
4use crate::api::{
5    CreateDnsRecordRequest, CreateDnsRecordResponse, DnsRecord, DnsRecordId, GeneralResponse,
6    ListDnsRecordsResponse, UpdateDnsRecordRequest,
7};
8
9/// Error type for the Simply.com DNS API client.
10///
11/// Represents possible errors when interacting with the Simply.com DNS API.
12#[derive(Debug, Error)]
13pub enum SimplyClientError {
14    /// There was an error with the HTTP request (network, invalid response, etc.).
15    #[error("HTTP error: {0}")]
16    Http(#[from] reqwest::Error),
17    /// The response could not be parsed from JSON. Usually returned if the Simply.com API returns an unexpected or malformed JSON body.
18    #[error("JSON deserialization error: {0}")]
19    Json(#[from] serde_json::Error),
20    /// The Simply.com API returned an error status or message. The first value is the status code, the second is the message returned by the API.
21    #[error("API error: {0} ({1})")]
22    Api(u32, String),
23}
24
25/// Async client for the Simply.com DNS API.
26///
27/// Provides methods to interact with DNS records using the Simply.com API.
28/// See: https://www.simply.com/en/docs/api/
29///
30/// Example usage:
31/// ```rust
32/// let client = SimplyClient::new("account", "api_key");
33/// // ...
34/// ```
35pub struct SimplyClient {
36    account: String,
37    api_key: String,
38    base_url: String,
39    client: Client,
40}
41
42impl SimplyClient {
43    /// Create a new Simply.com DNS API client instance.
44    ///
45    /// # Arguments
46    /// * `account` - Your Simply.com account identifier.
47    /// * `api_key` - The API key for authentication.
48    ///
49    /// For usage details, see: https://www.simply.com/en/docs/api/
50    pub fn new(account: impl Into<String>, api_key: impl Into<String>) -> Self {
51        Self {
52            account: account.into(),
53            api_key: api_key.into(),
54            base_url: "https://api.simply.com/2/".to_string(),
55            client: Client::new(),
56        }
57    }
58}
59
60impl SimplyClient {
61    /// List all DNS records for a given domain.
62    ///
63    /// # Arguments
64    /// * `domain` - The domain to list DNS records for.
65    ///
66    /// See: https://www.simply.com/en/docs/api/
67    pub async fn list_dns_records(
68        &self,
69        domain: &str,
70    ) -> Result<Vec<DnsRecord>, SimplyClientError> {
71        let url = format!(
72            "{}/my/products/{}/dns/records",
73            self.base_url.trim_end_matches('/'),
74            domain
75        );
76        let res = self
77            .client
78            .get(&url)
79            .basic_auth(&self.account, Some(&self.api_key))
80            .send()
81            .await?;
82        let resp: ListDnsRecordsResponse = res.json().await?;
83        Ok(resp.records.into_iter().map(|r| r.into()).collect())
84    }
85    /// Create a new DNS record for a domain.
86    ///
87    /// # Arguments
88    /// * `domain` - The domain to create the DNS record under.
89    /// * `req` - The DNS record request payload.
90    ///
91    /// See: https://www.simply.com/en/docs/api/
92    pub async fn create_dns_record(
93        &self,
94        domain: &str,
95        req: CreateDnsRecordRequest,
96    ) -> Result<Vec<DnsRecordId>, SimplyClientError> {
97        let url = format!(
98            "{}/my/products/{}/dns/records",
99            self.base_url.trim_end_matches('/'),
100            domain
101        );
102        let res = self
103            .client
104            .post(&url)
105            .basic_auth(&self.account, Some(&self.api_key))
106            .json(&req)
107            .send()
108            .await?;
109        let resp: CreateDnsRecordResponse = res.json().await?;
110        Ok(resp.record.unwrap_or_default())
111    }
112
113    /// Update an existing DNS record for a domain.
114    ///
115    /// # Arguments
116    /// * `domain` - The domain the DNS record belongs to.
117    /// * `record_id` - The ID of the DNS record to update.
118    /// * `req` - The updated DNS record payload.
119    ///
120    /// See: https://www.simply.com/en/docs/api/
121    pub async fn update_dns_record(
122        &self,
123        domain: &str,
124        record_id: DnsRecordId,
125        req: UpdateDnsRecordRequest,
126    ) -> Result<(), SimplyClientError> {
127        let url = format!(
128            "{}/my/products/{}/dns/records/{}",
129            self.base_url.trim_end_matches('/'),
130            domain,
131            record_id.id,
132        );
133        let res = self
134            .client
135            .put(&url)
136            .basic_auth(&self.account, Some(&self.api_key))
137            .json(&req)
138            .send()
139            .await?;
140        let status = res.status();
141        if !status.is_success() {
142            let resp: GeneralResponse = res.json().await?;
143            return Err(SimplyClientError::Api(
144                status.as_u16().into(),
145                resp.message.unwrap_or_default(),
146            ));
147        }
148        Ok(())
149    }
150
151    /// Delete a DNS record for a domain.
152    ///
153    /// # Arguments
154    /// * `domain` - The domain the DNS record belongs to.
155    /// * `record_id` - The ID of the DNS record to delete.
156    ///
157    /// See: https://www.simply.com/en/docs/api/
158    pub async fn delete_dns_record(
159        &self,
160        domain: &str,
161        record_id: DnsRecordId,
162    ) -> Result<(), SimplyClientError> {
163        let url = format!(
164            "{}/my/products/{}/dns/records/{}",
165            self.base_url.trim_end_matches('/'),
166            domain,
167            record_id.id,
168        );
169        let res = self
170            .client
171            .delete(&url)
172            .basic_auth(&self.account, Some(&self.api_key))
173            .send()
174            .await
175            .map_err(SimplyClientError::Http)?;
176        let status = res.status();
177        if !status.is_success() {
178            let resp: GeneralResponse = res.json().await?;
179            return Err(SimplyClientError::Api(
180                status.as_u16().into(),
181                resp.message.unwrap_or_default(),
182            ));
183        }
184        Ok(())
185    }
186}