X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/ae969bee7965e5c4d05ef05394d1d663c274c631..dbafdb904b48e72620f066a5c7cb0a123fc64baa:/validate.el diff --git a/validate.el b/validate.el index 3afe7b7a5..9eebe9a36 100644 --- a/validate.el +++ b/validate.el @@ -4,25 +4,59 @@ ;; Author: Artur Malabarba ;; Keywords: lisp +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) +;; Version: 0.1 -;; This program is free software; you can redistribute it and/or modify +;;; Commentary: +;; +;; This library offers two functions that perform schema validation. +;; Use this is your Elisp packages to provide very informative error +;; messages when your users accidentally misconfigure a variable. +;; For instance, if everything is fine, these do the same thing: +;; +;; 1. (validate-variable 'cider-known-endpoints) +;; 2. cider-known-endpoints +;; +;; However, if the user has misconfigured this variable, option +;; 1. will immediately give them an informative error message, while +;; option 2. won't say anything and will lead to confusing errors down +;; the line. +;; +;; The format and language of the schemas is the same one used in the +;; `:type' property of a `defcustom'. +;; +;; See: (info "(elisp) Customization Types") +;; +;; Both functions throw a `user-error' if the value in question +;; doesn't match the schema, and return the value itself if it +;; matches. The function `validate-variable' verifies whether the value of a +;; custom variable matches its custom-type, while `validate-value' checks an +;; arbitrary value against an arbitrary schema. +;; +;; Missing features: `:inline', `plist', `coding-system', `color', +;; `hook', `restricted-sexp'. + +;;; License: +;; +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, +;; +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - ;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . ;;; Code: +(require 'cl-lib) +(require 'seq) +(require 'cus-edit) (defun validate--check-list-contents (values schemas) "Check that all VALUES match all SCHEMAS." @@ -33,8 +67,7 @@ (defun validate--check (value schema) "Return nil if VALUE matches SCHEMA. If they don't match, return an explanation." - (let ((fail (list schema value)) - (args (cdr-safe schema)) + (let ((args (cdr-safe schema)) (expected-type (or (car-safe schema) schema)) (props nil)) (while (and (keywordp (car args)) (cdr args)) @@ -42,9 +75,9 @@ If they don't match, return an explanation." (setq args (or (plist-get props :args) args)) (let ((r - (cl-labels ((wtype ;wrong-type - (t) (unless (funcall (intern (format "%sp" t)) value) - (format "not a %s" t)))) + (cl-labels ((wtype ;wrong-type + (tt) (unless (funcall (intern (format "%sp" tt)) value) + (format "not a %s" tt)))) ;; TODO: hook (top-level only). (cl-case expected-type ((sexp other) nil) @@ -52,7 +85,7 @@ If they don't match, return an explanation." ((not (boundp value)) "this symbol has no variable binding"))) ((integer number float string character symbol function boolean face) (wtype expected-type)) - (regexp (cond ((ignore-errors (string-match re "") t) nil) + (regexp (cond ((ignore-errors (string-match value "") t) nil) ((wtype 'string)) (t "not a valid regexp"))) (repeat (cond @@ -90,7 +123,7 @@ If they don't match, return an explanation." (t (validate--check value `(repeat (cons ,key-type ,value-type))))))) ;; TODO: `plist' - ((choice radio) (if (not (cdr choice)) + ((choice radio) (if (not (cdr args)) (error "`choice' needs at least one argument") (let ((gather (mapcar (lambda (x) (validate--check value x)) args))) (when (seq-every-p #'identity gather) @@ -142,5 +175,12 @@ with `validate-value'. NOERROR is passed to `validate-value'." (if noerror val (error "Variable `%s' has no custom-type." symbol))))) +;;;###autoload +(defun validate-mark-safe-local (symbol) + "Mark SYMBOL as a safe local if its custom type is obeyed." + (put symbol 'safe-local-variable + (lambda (val) + (validate-value val (custom-variable-type symbol) 'noerror)))) + (provide 'validate) ;;; validate.el ends here