mu-scm: implement store->alist

Get information about the store as an alist.

Scm + cc + test + doc.
This commit is contained in:
Dirk-Jan C. Binnema
2025-07-09 21:50:31 +03:00
parent 8d46f80bb9
commit 6d72aa5c7f
5 changed files with 100 additions and 15 deletions

View File

@ -38,6 +38,57 @@ to_store(SCM scm, const char *func, int pos)
return *reinterpret_cast<Store*>(scm_foreign_object_ref(scm, 0));
}
static SCM
subr_cc_store_alist(SCM store_scm) try {
constexpr auto func{"cc-store-alist"};
SCM alist{SCM_EOL};
const auto& conf{to_store(store_scm, func, 1).config()};
using MuConfig = Mu::Config;
using Type = MuConfig::Type;
for (const auto& prop: Mu::Config::properties) {
// don't expose internal values & values that may change during
// runtime
if (any_of(prop.flags &
(MuConfig::Flags::Internal | MuConfig::Flags::Runtime)))
continue;
const auto str{conf.get_str(prop)};
if (str.empty())
continue;
const auto name{make_symbol(prop.name)};
const auto val = std::invoke([&]() {
switch (prop.type) {
case Type::Number:
return to_scm(MuConfig::decode<Type::Number>(str));
case Type::Boolean:
return to_scm(MuConfig::decode<Type::Boolean>(str));
case Type::Timestamp:
return to_scm(MuConfig::decode<Type::Timestamp>(str));
case Type::Path:
return to_scm(MuConfig::decode<Type::Path>(str));
case Type::String:
return to_scm(MuConfig::decode<Type::String>(str));
case Type::StringList:
return to_scm(MuConfig::decode<Type::StringList>(str));
default:
throw ScmError{ScmError::Id::WrongType, func, 1, store_scm, "store"};
}
});
alist = scm_acons(name, val, alist);
}
return scm_reverse_x(alist, SCM_EOL);
} catch (const ScmError& err) {
err.throw_scm();
}
static SCM
subr_cc_store_mcount(SCM store_scm) try {
return to_scm(to_store(store_scm, "cc-store-mcount", 1).size());
@ -139,6 +190,9 @@ init_subrs()
reinterpret_cast<scm_t_subr>(subr_cc_store_mcount));
scm_c_define_gsubr("cc-store-cfind", 5/*req*/, 0/*opt*/, 0/*rst*/,
reinterpret_cast<scm_t_subr>(subr_cc_store_cfind));
scm_c_define_gsubr("cc-store-alist", 1/*req*/, 0/*opt*/, 0/*rst*/,
reinterpret_cast<scm_t_subr>(subr_cc_store_alist));
#pragma GCC diagnostic pop
}
@ -167,8 +221,8 @@ Mu::Scm::init_store(const Store& store)
SCM
Mu::Scm::to_scm(const Contact& contact)
{
static SCM email{scm_from_utf8_symbol("email")};
static SCM name{scm_from_utf8_symbol("name")};
static SCM email{make_symbol("email")};
static SCM name{make_symbol("name")};
SCM alist = scm_acons(email, to_scm(contact.email), SCM_EOL);
if (!contact.name.empty())

View File

@ -3,14 +3,18 @@
(use-modules (mu) (srfi srfi-64)
(ice-9 textual-ports))
(define (test-basic)
(test-begin "test-basic")
(define (test-store)
(test-begin "test-store")
(test-equal "mcount" 19 (mcount))
(test-equal "cfind" 29 (length (cfind "")))
(test-equal "mfind" 19 (length (mfind "")))
(test-end "test-basic"))
(let ((info (store->alist)))
(test-equal 50000 (assoc-ref info 'batch-size))
(test-equal 100000000 (assoc-ref info 'max-message-size)))
(test-end "test-store"))
(define (test-basic-mfind)
@ -33,10 +37,8 @@
(let ((recip (car (to msg))))
(test-equal "Bilbo Baggins" (assoc-ref recip 'name))
(test-equal "bilbo@anotherexample.com" (assoc-ref recip 'email)))
;; no date
(test-assert (not (date msg)))
;; flags
(test-equal '(unread) (flags msg))
(test-assert (unread? msg))
@ -171,7 +173,7 @@
(test-with-runner runner
(test-begin "mu-scm-tests")
(test-basic)
(test-store)
(test-basic-mfind)
(test-mfind)
(test-message-full)

View File

@ -221,6 +221,13 @@ namespace Mu::Scm {
return scm_from_utf8_stringn(val.data(), val.size());
else if constexpr (is_char_array_v<Type>|| std::is_same_v<Type, const char*>)
return scm_from_utf8_string(val);
else if constexpr (std::is_same_v<Type, std::vector<std::string>>) {
SCM lst{SCM_EOL};
for (const auto& s: val)
lst = scm_append_x(scm_list_2(lst,
scm_list_1(to_scm(s))));
return lst;
}
else if constexpr (std::is_same_v<Type, bool>)
return scm_from_bool(val);
else if constexpr (std::is_same_v<Type, size_t>)

View File

@ -87,6 +87,7 @@
mfind
mcount
cfind
store->alist
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Other
@ -453,7 +454,8 @@ This is a list of <mime-part> objects."
;; the 'store-object' is a foreign object wrapping a const Store*.
(define-class <store> ()
(store-object #:init-keyword #:store-object #:getter store-object))
(store-object #:init-keyword #:store-object #:getter store-object)
(alist #:init-value #f))
;; not exported
(define-method (make-store store-object)
@ -464,6 +466,14 @@ This is a list of <mime-part> objects."
;; %default-store-object is defined in mu-scm-store.cc
(make-store %default-store-object))
(define* (store->alist #:key (store %default-store))
"Get an alist-representation for some store.
Keyword arguments:
#:store %default-store. Leave at default."
(when (not (slot-ref store 'alist))
(slot-set! store 'alist (cc-store-alist (store-object store))))
(slot-ref store 'alist))
(define* (mfind query
#:key
(store %default-store)

View File

@ -260,7 +260,7 @@ few key concepts, represented in some GOOP objects and other data-structures:
@end itemize
@menu
* Store:: the database of all information
* Store:: where message information lives
* Message:: inspecting individual messages
* Miscellaneous:: other functions
* Helpers:: some helper functions
@ -352,6 +352,18 @@ Example usage:
=> 140728
@end lisp
@deffn {Scheme Procedure} store->alist
@end deffn
Retrieve an association list (``alist'') with information about the store.
Example:
@lisp
(store->alist)
=> ((batch-size . 50000) (created . 1741180008) (max-message-size . 100000000)
(personal-addresses "djcb@@example.com" "msx@@example.com")
(root-maildir . "/home/user/Maildir") (schema-version . 500))
@end lisp
@node Message
@section Message