use std::fmt; use anyhow::{Context, Result}; use clap::ValueEnum; use crate::dns::{ForeignRecord, Name, Record}; use crate::reconcile::Action; use crate::{cloudflare, wix}; /// Where a zone is hosted. #[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)] pub enum Kind { Cloudflare, Wix, } impl Kind { pub fn connect(self) -> Result> { let var = |name: &str, hint: &str| { std::env::var(name).with_context(|| format!("set {name} to {hint}")) }; Ok(match self { Kind::Cloudflare => Box::new(cloudflare::Client::new( var("CF_API_TOKEN", "a Cloudflare API token") .or_else(|e| std::env::var("CLOUDFLARE_API_TOKEN").map_err(|_| e))?, )), Kind::Wix => Box::new(wix::Client::new( var("WIX_API_TOKEN", "a Wix account-level API token")?, var( "WIX_ACCOUNT_ID", "the Wix account ID shown beside your API keys", )?, )), }) } } impl fmt::Display for Kind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str( self.to_possible_value() .expect("no skipped variants") .get_name(), ) } } /// A DNS host. Providers address records differently (Cloudflare by ID, Wix /// by whole RRset), so `apply` takes plain record changes and resolves them /// against the zone as it stands at apply time. pub trait Provider { fn list_zones(&self) -> Result>; fn records(&self, zone: &Name) -> Result>; fn apply(&self, zone: &Name, actions: &[Action]) -> Result<()>; } /// A record at the provider: managed records are reconciled, everything else /// is kept only for display. pub enum Listed { Managed(Record), Foreign(ForeignRecord), } /// Split the provider's records into the ones we manage and the ones we /// leave alone. Apex NS records belong to the provider's nameservers and /// are untouchable, so they count as foreign too. pub fn split_listed(listed: Vec, apex: &Name) -> (Vec, Vec) { let (mut managed, mut foreign) = (Vec::new(), Vec::new()); for l in listed { match l { Listed::Managed(r) if r.is_apex_ns(apex) => { foreign.push(ForeignRecord { name: r.name.to_string(), rtype: r.rtype().to_string(), ttl: r.ttl, content: r.data.to_string(), }); } Listed::Managed(r) => managed.push(r), Listed::Foreign(f) => foreign.push(f), } } (managed, foreign) }