mu-scm: implement message->alist
Implement message->alist; i.e. to convert the mu4e-style plist into an idiomatic alist. Add it as a message slot, initializing it lazily. Update the message accessors to use the alist. Add tests, docs.
This commit is contained in:
@ -82,13 +82,21 @@
|
||||
(references msg))
|
||||
(test-equal "439C1136.90504@euler.org" (thread-id msg)))
|
||||
|
||||
(let ((msg (car (mfind "subject:\"gcc include search order\""))))
|
||||
(let* ((msg (car (mfind "subject:\"gcc include search order\"")))
|
||||
(alist (message->alist msg)))
|
||||
(test-equal "gcc include search order" (subject msg))
|
||||
(test-equal "klub" (header msg "precedence"))
|
||||
(test-equal "gcc-help.gcc.gnu.org" (mailing-list msg))
|
||||
(test-equal #f (references msg))
|
||||
(test-equal "3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com" (message-id msg))
|
||||
(test-equal "3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com" (thread-id msg)))
|
||||
(test-equal "3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com" (thread-id msg))
|
||||
|
||||
;; alist
|
||||
(test-equal "gcc include search order" (assoc-ref alist 'subject))
|
||||
(test-equal 'normal (assoc-ref alist 'priority))
|
||||
(test-equal '((email . "anon@example.com") (name . "Mickey Mouse"))
|
||||
(car (assoc-ref alist 'from))))
|
||||
|
||||
(test-end "test-message-more"))
|
||||
|
||||
|
||||
|
||||
101
scm/mu-scm.scm
101
scm/mu-scm.scm
@ -35,8 +35,10 @@
|
||||
<message>
|
||||
make-message
|
||||
|
||||
message->alist
|
||||
|
||||
date
|
||||
last-change
|
||||
changed
|
||||
|
||||
message-id
|
||||
path
|
||||
@ -108,9 +110,7 @@
|
||||
(define (set-documentation! symbol docstring)
|
||||
"Set the docstring for symbol in current module to docstring.
|
||||
This is useful for symbols that do not support docstrings directly, such
|
||||
add
|
||||
(define foo 123)
|
||||
"
|
||||
as (define foo 123)."
|
||||
;; https://git.wolfsden.cz/guile-wolfsden/tree/wolfsden/documentation.scm
|
||||
(set-object-property! (module-ref (current-module) symbol)
|
||||
'documentation docstring))
|
||||
@ -144,15 +144,29 @@ If not found, return #f."
|
||||
(string->symbol (string-drop name 1))
|
||||
sym)))
|
||||
|
||||
(define (emacs-time->epoch-secs lst)
|
||||
"Convert emacs-style timestamp LST to a number of seconds since epoch.
|
||||
If LST is #f, return #f."
|
||||
(if lst
|
||||
(+ (ash (car lst) 16) (cadr lst))
|
||||
#f))
|
||||
|
||||
(define (plist->alist plist)
|
||||
"Convert a plist into an alist."
|
||||
"Convert a plist into an alist.
|
||||
This is specific for message plists."
|
||||
(let ((alist '()))
|
||||
(plist-for-each
|
||||
(lambda (k v)
|
||||
(set! alist
|
||||
(append! alist
|
||||
(list (cons (decolonize-symbol k)
|
||||
v)))))
|
||||
(let ((key (decolonize-symbol k)))
|
||||
(set! alist
|
||||
(append! alist
|
||||
(list (cons key
|
||||
(cond
|
||||
((member key '(from to cc bcc))
|
||||
(map plist->alist v))
|
||||
((member key '(date changed))
|
||||
(emacs-time->epoch-secs v))
|
||||
(else v))))))))
|
||||
plist)
|
||||
alist))
|
||||
|
||||
@ -260,6 +274,12 @@ Only having a plist is cheaper.")
|
||||
(slot-set! message 'plist (cc-message-plist (cc-message message))))
|
||||
(slot-ref message 'plist))
|
||||
|
||||
(define-method (message->alist (message <message>))
|
||||
"Get an association-list (alist) representation for MESSAGE."
|
||||
(when (not (slot-ref message 'alist))
|
||||
(slot-set! message 'alist (plist->alist (plist message))))
|
||||
(slot-ref message 'alist))
|
||||
|
||||
(define-method (cc-message (message <message>))
|
||||
"Get the foreign object for this MESSAGE.
|
||||
If MESSAGE does not have such an object yet, create it from the
|
||||
@ -268,9 +288,6 @@ path of the message."
|
||||
(slot-set! message 'cc-message (cc-message-make (path message))))
|
||||
(slot-ref message 'cc-message))
|
||||
|
||||
(define-method (find-field (message <message>) field)
|
||||
(plist-find (plist message) field))
|
||||
|
||||
(define-method (sexp (message <message>))
|
||||
"Get the s-expression (plist) for this MESSAGE.
|
||||
|
||||
@ -278,51 +295,44 @@ This is an internal data-structure, originally for use with mu4e, but useful
|
||||
here as well. However, the precise details are not part of mu-scm API."
|
||||
(plist message))
|
||||
|
||||
(define (emacs-time->epoch-secs lst)
|
||||
"Convert emacs-style timestamp LST to a number of seconds since epoch.
|
||||
If LST is #f, return #f."
|
||||
(if lst
|
||||
(+ (ash (car lst) 16) (cadr lst))
|
||||
#f))
|
||||
|
||||
;; Accessors for the fields
|
||||
|
||||
(define-method (subject (message <message>))
|
||||
"Get the subject for MESSAGE or #f if not found."
|
||||
(find-field message ':subject))
|
||||
(assoc-ref (message->alist message) 'subject))
|
||||
|
||||
(define-method (maildir (message <message>))
|
||||
"Get the maildir for MESSAGE or #f if not found."
|
||||
(find-field message ':maildir))
|
||||
(assoc-ref (message->alist message) 'maildir))
|
||||
|
||||
(define-method (message-id (message <message>))
|
||||
"Get the message-id for MESSAGE or #f if not found."
|
||||
(find-field message ':message-id))
|
||||
(assoc-ref (message->alist message) 'message-id))
|
||||
|
||||
(define-method (date (message <message>))
|
||||
"Get the date for MESSAGE was sent.
|
||||
"Get the timestamp for MESSAGE was sent.
|
||||
This is the number of seconds since epoch; #f if not found."
|
||||
(emacs-time->epoch-secs (find-field message ':date)))
|
||||
(assoc-ref (message->alist message) 'date))
|
||||
|
||||
(define-method (last-change (message <message>))
|
||||
"Get the date for the last change to MESSAGE.
|
||||
(define-method (changed (message <message>))
|
||||
"Get the timestamp for the last change to MESSAGE.
|
||||
This is the number of seconds since epoch; #f if not found."
|
||||
(emacs-time->epoch-secs (find-field message ':changed)))
|
||||
(assoc-ref (message->alist message) 'changed))
|
||||
|
||||
(define-method (path (message <message>))
|
||||
"Get the file-system path for MESSAGE.
|
||||
A symbol, either 'high, 'low or 'normal, or #f if not found."
|
||||
(find-field message ':path))
|
||||
(assoc-ref (message->alist message) 'path))
|
||||
|
||||
(define-method (priority (message <message>))
|
||||
"Get the priority for MESSAGE.
|
||||
A symbol, either 'high, 'low or 'normal, or #f if not found."
|
||||
(find-field message ':priority))
|
||||
(assoc-ref (message->alist message) 'priority))
|
||||
|
||||
(define-method (language (message <message>))
|
||||
"Get the ISO-639-1 language code for the MESSAGE as a symbol, if detected.
|
||||
Return #f otherwise."
|
||||
(let ((lang (find-field message ':language)))
|
||||
(let ((lang ( (assoc-ref (message->alist message) 'language))))
|
||||
(if lang
|
||||
(string->symbol lang)
|
||||
#f)))
|
||||
@ -330,7 +340,7 @@ Return #f otherwise."
|
||||
|
||||
(define-method (size (message <message>))
|
||||
"Get the size of the MESSAGE in bytes or #f if not available."
|
||||
(find-field message ':size))
|
||||
(assoc-ref (message->alist message) 'size))
|
||||
|
||||
(define-method (references (message <message>))
|
||||
"Get the list of reference of MESSAGE or #f if not available.
|
||||
@ -338,7 +348,7 @@ Return #f otherwise."
|
||||
reference (message-id) will appear at most once, duplicates and
|
||||
fake-message-id (see impls) are filtered out. If there are no references, return
|
||||
#f."
|
||||
(find-field message ':references))
|
||||
(assoc-ref (message->alist message) 'references))
|
||||
|
||||
(define-method (thread-id (message <message>))
|
||||
"Get the oldest (first) reference for MESSAGE, or message-id if there are none.
|
||||
@ -351,20 +361,19 @@ This is method is useful to determine the thread a message is in."
|
||||
|
||||
(define-method (mailing-list (message <message>))
|
||||
"Get the mailing-list id for MESSAGE or #f if not available."
|
||||
(find-field message ':list))
|
||||
(assoc-ref (message->alist message) 'list))
|
||||
|
||||
;; Flags.
|
||||
|
||||
(define-method (flags (message <message>))
|
||||
"Get the size of the MESSAGE in bytes or #f if not available."
|
||||
(find-field message ':flags))
|
||||
(assoc-ref (message->alist message) 'flags))
|
||||
|
||||
(define-method (flag? (message <message>) flag)
|
||||
"Does MESSAGE have FLAG?"
|
||||
(let ((flags
|
||||
(find-field message ':flags)))
|
||||
(if flags
|
||||
(if (member flag flags) #t #f)
|
||||
(let ((flgs (flags message)))
|
||||
(if flgs
|
||||
(if (member flag flgs) #t #f)
|
||||
#f)))
|
||||
|
||||
(define-method (draft? (message <message>))
|
||||
@ -423,31 +432,23 @@ This is method is useful to determine the thread a message is in."
|
||||
"Does MESSAGE have a calender invitation?"
|
||||
(flag? message 'calendar))
|
||||
|
||||
(define-method (find-contact-field (message <message>) field)
|
||||
"Get contact FIELD from MESSAGE as an alist.
|
||||
Helper method "
|
||||
(let ((cs (find-field message field)))
|
||||
(if cs
|
||||
(map plist->alist cs)
|
||||
#f)))
|
||||
|
||||
(define-method (from (message <message>))
|
||||
"Get the sender (the From: field) for MESSAGE or #f if not found."
|
||||
(find-contact-field message ':from))
|
||||
(assoc-ref (message->alist message) 'from))
|
||||
|
||||
(define-method (to (message <message>))
|
||||
"Get the (intended) recipient for MESSAGE (the To: field) or #f if not found."
|
||||
(find-contact-field message ':to))
|
||||
(assoc-ref (message->alist message) 'to))
|
||||
|
||||
(define-method (cc (message <message>))
|
||||
"Get the (intended) carbon-copy recipient for MESSAGE (the Cc: field) or #f if
|
||||
not found."
|
||||
(find-contact-field message ':cc))
|
||||
(assoc-ref (message->alist message) 'cc))
|
||||
|
||||
(define-method (bcc (message <message>))
|
||||
"Get the (intended) blind carbon-copy recipient for MESSAGE (the Bcc: field) or
|
||||
#f if not found."
|
||||
(find-contact-field message ':bcc))
|
||||
(assoc-ref (message->alist message) 'bcc))
|
||||
|
||||
(define* (body message #:key (html? #f))
|
||||
"Get the MESSAGE body or #f if not found
|
||||
|
||||
@ -658,13 +658,13 @@ Is this a personal message? Returns @t{#t} or @t{#f}.
|
||||
|
||||
@deffn {Scheme Procedure} calendar? message
|
||||
@end deffn
|
||||
Does this message include a calendar invitation? Returns @t{#t} or @t{#f}.
|
||||
Does this message include a calendar invitation? Returns @t{#t} or @code{#f}.
|
||||
|
||||
@subsection Miscellaneous
|
||||
|
||||
@deffn {Scheme Procedure} last-change message
|
||||
@deffn {Scheme Procedure} changed message
|
||||
@end deffn
|
||||
Get the time of the message's last change (through @t{mu}), or @t{#f} if there
|
||||
Get the time of the message's last change (through @t{mu}), or @code{#f} if there
|
||||
is none. The time is expressed the data as the number of seconds since epoch,
|
||||
@t{time_t}.
|
||||
|
||||
@ -677,7 +677,7 @@ For example:
|
||||
@deffn {Scheme Procedure} priority message
|
||||
@end deffn
|
||||
Get the message's priority. This is a symbol, either @t{high}, @t{normal} or
|
||||
@t{low}, or @t{#f} if not present.
|
||||
@t{low}, or @code{#f} if not present.
|
||||
|
||||
For example:
|
||||
@lisp
|
||||
@ -697,7 +697,7 @@ For example:
|
||||
|
||||
@deffn {Scheme Procedure} language message
|
||||
@end deffn
|
||||
Get the ISO-639-1 language code for message's primary language or @t{#f} if not
|
||||
Get the ISO-639-1 language code for message's primary language or @code{#f} if not
|
||||
found. This is available only if @t{mu} was built with CLD2 support, see
|
||||
@command{mu info}. The language code is represented as a symbol, such as @t{en},
|
||||
@t{nl} or @t{fi}.
|
||||
@ -833,7 +833,7 @@ Convert a @t{time_t} value (``seconds-since-epoch'') to a string. The optional
|
||||
output format, while the @code{#:utc?} determines whether to use UTC.
|
||||
@c Defaults are determined by the @code{%preferences} variable.
|
||||
|
||||
If @var{time_t} is @t{#f}, return @code{#f}.
|
||||
If @var{time_t} is @code{#f}, return @code{#f}.
|
||||
|
||||
@node GNU Free Documentation License
|
||||
@appendix GNU Free Documentation License
|
||||
|
||||
Reference in New Issue
Block a user