diff --git a/tools/emacs-pkgs/defzone/defzone.el b/tools/emacs-pkgs/defzone/defzone.el index b67501cbd..ffd359e5f 100644 --- a/tools/emacs-pkgs/defzone/defzone.el +++ b/tools/emacs-pkgs/defzone/defzone.el @@ -8,38 +8,50 @@ "Evaluate a record definition and turn it into a zone file record in ZONE, optionally prefixed with SUBDOMAIN." - (declare (indent defun)) ; TODO(tazjin): remove - (let ((name (if subdomain (s-join "." (list subdomain zone)) zone))) - (pcase record - (`(SOA . (,ttl . (,mname ,rname ,serial ,refresh ,retry ,expire ,min))) - (format "%s %s IN SOA %s %s %s %s %s %s %s" - name ttl mname rname serial refresh retry expire min)) + (cl-labels ((plist->alist (plist) + (when plist + (cons + (cons (car plist) (cadr plist)) + (plist->alist (cddr plist)))))) + (let ((name (if subdomain (s-join "." (list subdomain zone)) zone))) + (pcase record + ;; SOA RDATA (RFC 1035; 3.3.13) + ((and `(SOA . (,ttl . ,keys)) + (let (map (:mname mname) (:rname rname) (:serial serial) + (:refresh refresh) (:retry retry) (:expire expire) + (:minimum min)) + (plist->alist keys))) + (if-let ((missing (-filter #'null (not (list mname rname serial + refresh retry expire min))))) + (error "Missing fields in SOA record: %s" missing) + (format "%s %s IN SOA %s %s %s %s %s %s %s" + name ttl mname rname serial refresh retry expire min))) - (`(NS . (,ttl . ,targets)) - (->> targets - (-map (lambda (target) (format "%s %s IN NS %s" name ttl target))) - (s-join "\n"))) + (`(NS . (,ttl . ,targets)) + (->> targets + (-map (lambda (target) (format "%s %s IN NS %s" name ttl target))) + (s-join "\n"))) - (`(MX . (,ttl . ,pairs)) - (->> pairs - (-map (-lambda ((preference . exchange)) - (format "%s %s IN MX %s %s" name ttl preference exchange))) - (s-join "\n"))) + (`(MX . (,ttl . ,pairs)) + (->> pairs + (-map (-lambda ((preference . exchange)) + (format "%s %s IN MX %s %s" name ttl preference exchange))) + (s-join "\n"))) - (`(TXT ,ttl ,text) (format "%s %s IN TXT %s" name ttl (prin1-to-string text))) + (`(TXT ,ttl ,text) (format "%s %s IN TXT %s" name ttl (prin1-to-string text))) - (`(A . (,ttl . ,ips)) - (->> ips - (-map (lambda (ip) (format "%s %s IN A %s" name ttl ip))) - (s-join "\n"))) + (`(A . (,ttl . ,ips)) + (->> ips + (-map (lambda (ip) (format "%s %s IN A %s" name ttl ip))) + (s-join "\n"))) - (`(CNAME ,ttl ,target) (format "%s %s IN CNAME %s" name ttl target)) + (`(CNAME ,ttl ,target) (format "%s %s IN CNAME %s" name ttl target)) - ((and `(,sub . ,records) - (guard (stringp sub))) - (s-join "\n" (-map (lambda (r) (record-to-record zone r sub)) records))) + ((and `(,sub . ,records) + (guard (stringp sub))) + (s-join "\n" (-map (lambda (r) (record-to-record zone r sub)) records))) - (_ (error "Invalid record definition: %s" record))))) + (_ (error "Invalid record definition: %s" record)))))) (defmacro defzone (fqdn &rest records) "Generate zone file for the zone at FQDN from a simple DSL." diff --git a/tools/emacs-pkgs/defzone/example.el b/tools/emacs-pkgs/defzone/example.el index 6405eaf2f..e9c86d25e 100644 --- a/tools/emacs-pkgs/defzone/example.el +++ b/tools/emacs-pkgs/defzone/example.el @@ -1,8 +1,14 @@ ;;; example.el - usage example for defzone macro (defzone "tazj.in." - (SOA 21600 "ns-cloud-a1.googledomains.com." "cloud-dns-hostmaster.google.com." - 123 21600 3600 1209600 300) + (SOA 21600 + :mname "ns-cloud-a1.googledomains.com." + :rname "cloud-dns-hostmaster.google.com." + :serial 123 + :refresh 21600 + :retry 3600 + :expire 1209600 + :minimum 300) (NS 21600 "ns-cloud-a1.googledomains.com."