zone-update 0.10.1

A library of CRUD-like operations on DNS zones for multiple providers
Documentation
# Zone Update

[![Crates.io](https://img.shields.io/crates/v/zone-update)](https://crates.io/crates/zone-update)
[![Docs.rs](https://docs.rs/zone-update/badge.svg)](https://docs.rs/zone-update)
[![GitHub CI](https://github.com/tarka/zone-update/actions/workflows/tests.yml/badge.svg)](https://github.com/tarka/zone-update/actions)
[![License](https://img.shields.io/crates/l/zone-update)](https://github.com/tarka/zone-update/blob/main/README.md#License)

A minimal Rust library providing CRUD-like operations on DNS records with various DNS providers.

## Overview

Zone Update is a lightweight library that provides a simple interface for
programmatically managing DNS zone records through provider APIs.
The library is both blocking and async, and supports multiple async runtimes
(see below).

### Supported DNS providers

Currently, Zone Update supports the following DNS providers:

* Cloudflare
* deSEC
* DigitalOcean
* Dnsimple
* DnsMadeEasy
* Gandi
* Linode
* Porkbun

See the [DNS providers matrix](docs/PROVIDERS.md) for more details.

### Supported platforms

`zone-update` supports both blocking and async APIs. For async the library
attempts to be as provider-agnostic; it is known (tested) to work with the
following runtimes:

* [tokio]https://tokio.rs/
* [smol]https://docs.rs/smol/
* [compio]https://compio.rs/
* [monoio]https://github.com/bytedance/monoio
* [glommio]https://github.com/DataDog/glommio

`zone-update` is tested against Linux, Mac & Windows.

### Feature flags

Each DNS provider is gated behind their name, however all provider are enabled
by default. To limit the providers you can add `zone-update` to your
`Cargo.toml` in the following format:

```text
zone-update = { version = "*", default-features = false, features = ["digitalocean", "desec"] }
```

The other notable flag is `async`, which is not enabled by default.

## Usage

### Basic Example

```rust
use zone_update::{gandi, DnsProvider, errors::Result};
use std::net::Ipv4Addr;

fn update_gandi_record() -> Result<()> {
    let config = zone_update::Config {
        domain: "example.com".to_string(),
        dry_run: false,
    };
    
    let auth = gandi::Auth::ApiKey("your-api-key".to_string());
    let client = gandi::Gandi::new(config, auth);
    
    let host = "www";
    let new_ip = Ipv4Addr::new(192, 0, 2, 1);

    // Update the A record for www.example.com
    client.update_a_record(host, &new_ip)?;
    
    Ok(())
}
```

### Provider lookup from configuration file

The `Provider` enum supports deserialisation with serde, which allows runtime
lookup of DNS providers from a configuration file:

```rust
use serde::Deserialize;
use zone_update::{Provider, errors::Result};
use std::net::Ipv4Addr;

const CONFIG_TOML: &str = r#"
domain = "example.com"
dry_run = true

[provider]
name = "porkbun"
key = "my_key"
secret = "my_secret"
"#;

#[derive(Deserialize)]
pub struct MyConfig {
    domain: String,
    dry_run: bool,
    provider: Provider,
}

fn update_website_record() -> Result<()> {
    let config: MyConfig = toml::from_str(CONFIG_TOML).unwrap();

    let zu_config = zone_update::Config {
        domain: config.domain,
        dry_run: config.dry_run,
    };

    let client = config.provider
        .blocking_impl(zu_config);

    let host = "www";
    let new_ip = Ipv4Addr::new(192, 0, 2, 1);

    // Update the A record for www.example.com
    client.update_a_record(host, &new_ip)?;

    Ok(())
}
```

See the `examples` directory for other use-cases.

## Contributing

At this point the most useful contributions would be to add additional DNS
provider APIs. However other contributions are welcome.

### Steps for adding a new provider

This needs to be expanded, and the existing implementations are currently your
best reference, but is a basic checklist:

* Create a new module directory under `src` and add `mod.rs` and (recommended)
  `types.rs`. Create the appropriate features flags in `Cargo.toml`.
* Add the necessary type structures with serde annotations; you will need to
  consult the API documentation and/or experiment with `curl`.
* Create a structure for the provider implementation (usually just wrapping
  `Auth` & `Config`.
* Implement the core CRUD operations in the `DnsProvider` trait for this
  struct. This tends to be provider-specific, but most follow on of several
  patterns. The existing implementations can be consulted as a reference.
* Use the `generate_helpers` macro to fill out the rest of the trait.
* Create a test module and use the `generate_tests` macro to create the standard
  tests.
* Run the tests against a sandbox or working account (_Do Not Skip This Step_).
* Once the blocking API implemented the async implementation is generated by
  macros. See an existing provider impl.
* Add you provider to the `Provider` enum and its impl.
* Raise a PR.

### AI Contribution Policy

This reason this project will not accept runtime code generated by
AI. Generation of _draft_ documentation and test code is acceptable, but should
be reviewed by the submitter before raising a PR.

## License

This project is licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE-2.0.txt)
- MIT license ([LICENSE-MIT]LICENSE-MIT)

at your option.