I'm writing a function which wraps org-export-as
for use in html conversion of a file. In this function, I define the options for org-export-with-toc
, org-export-with-section-numbers
, and org-html-htmlize-output-type
based on the optional arguments provided to the user. If the user fails to provide an argument, I supply a default.
(defun my-export (file &optional toc section-num output-type backend)
"Export FILE to html string using `org-export-as'.
This function wraps `org-export-as'. See that function for greater argument
details.
TOC and SECTION-NUM generate table of contents and section
numbers, respectively. Defaults for each are nil.
OUTPUT-TYPE is 'css, 'inline-css, or nil as defined by
`org-html-htmlize-output-type'. Default is 'css.
BACKEND is the export backend. Default is 'html."
(let* ((org-export-with-toc toc)
(org-export-with-section-numbers section-num)
(backend (or backend 'html))
;; Want 'css to be the default value here
(org-html-htmlize-output-type
(find output-type '(css inline-css nil))))
(converted
(with-temp-buffer
(insert-file-contents-literally file)
(org-export-as backend nil nil t nil)))) converted))
The trouble is this: org-html-htmlize-output-type
only accepts three values, 'css
, 'inline-css
, or nil
. However, when a user fails to provide an optional argument, nil
is passed. I have no way to discern if nil
was provided intentionally as the preferred OUTPUT-TYPE
or if it was simply ignored in favor of the default!
How is such a dilemma commonly handled?
One approach is to create a new value to represent nil
and use cond
to filter for the various choices:
(defun my-export (file &optional toc section-num output-type backend)
(let* ((org-export-with-toc toc)
(org-export-with-section-numbers section-num)
(backend (or backend 'html))
;; To toggle nil, user must specify 'plain-text
(org-html-htmlize-output-type
(cond ((eq output-type 'css) type)
((eq output-type 'inline-css) type)
((eq output-type 'plain-text) nil)
('css)))
(converted
(with-temp-buffer
(insert-file-contents-literally file)
(org-export-as backend nil nil t nil))))
converted))
Another thought I had was to use 'nil
instead of 'plain-text
but (un)fortunately (eq nil 'nil)
is t
.
Timing all the options, they are all on par.
(defun test-export-cond (file &optional arg1 arg2 arg3 opt4)
"Test export using cond."
(let* ((opt1 arg1)
(opt2 arg2)
(opt3 (cond ((eq arg3 'css) type)
((eq arg3 'inline-css) type)
((eq arg3 'plain-text) nil)
('css)))
(opt4 (or opt4 'html)))
(list file opt1 opt2 opt3 opt4)))
(defun test-export-if (file &rest rargs)
"Test export using if."
(let* ((nargs (length rargs))
(opt1 (nth 0 rargs))
(opt2 (nth 1 rargs))
(opt3
(if (< nargs 3)
'css
(nth 2 rargs)))
(opt4
(if (< nargs 4)
'html
(nth 3 rargs))))
(list file opt1 opt2 opt3 opt4)))
(cl-defun test-export-cl (file &optional arg1 arg2 (arg3 'css) (opt4 'html))
"Test export using cl-lib."
(let* ((opt1 arg1)
(opt2 arg2)
(opt3 arg3))
(list file opt1 opt2 opt3 opt4)))
(test-export-cond "~/file.txt")
(test-export-if "~/file.txt")
(test-export-cl "~/file.txt")
(defmacro test-measure-time (times &rest body)
"Measure the average time it takes to evaluate BODY."
`(let ((cur-time (current-time)))
(dotimes (i ,times)
,@body)
(message "%.06f" (/ (float-time (time-since cur-time))) ,times)))
(setq test-do-times 1000000)
(test-measure-time test-do-times (test-export-cond "~/file.txt")) ; "0.063467"
(test-measure-time test-do-times (test-export-if "~/file.txt")) ; "0.064669"
(test-measure-time test-do-times (test-export-cl "~/file.txt")) ; "0.066125"