scm: improve store->alist

Include all non-internal properties in the output alist.

Clean up some of the type-conversion (using concepts)

Fix conversion to bool
This commit is contained in:
Dirk-Jan C. Binnema
2026-07-22 15:24:10 +03:00
committed by Seth Ladygo
parent ae8607093b
commit 31b0fd45c2
3 changed files with 41 additions and 29 deletions

View File

@ -1,5 +1,5 @@
/* /*
** Copyright (C) 2025 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** Copyright (C) 2025-2026 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
** **
** This program is free software; you can redistribute it and/or modify it ** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the ** under the terms of the GNU General Public License as published by the
@ -50,16 +50,11 @@ subr_cc_store_alist(SCM store_scm) try {
for (const auto& prop: Mu::Config::properties) { for (const auto& prop: Mu::Config::properties) {
// don't expose internal values & values that may change during // don't expose internal/system values
// runtime if (any_of(prop.flags & (MuConfig::Flags::Internal|MuConfig::Flags::System)))
if (any_of(prop.flags &
(MuConfig::Flags::Internal | MuConfig::Flags::Runtime)))
continue; continue;
const auto str{conf.as_display_string(prop)}; const auto str{conf.as_display_string(prop)};
if (str.empty())
continue;
const auto name{make_symbol(prop.name)}; const auto name{make_symbol(prop.name)};
const auto val = std::invoke([&]() { const auto val = std::invoke([&]() {
switch (prop.type) { switch (prop.type) {

View File

@ -96,9 +96,19 @@ namespace Mu::Scm {
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html
template<typename T> struct always_false : std::false_type {}; template<typename T> struct always_false : std::false_type {};
template<typename T> constexpr bool is_char_array_v = template <typename T>
std::is_array_v<T> && concept IsCharArray =
std::is_same_v<std::remove_extent_t<T>, char>; std::is_array_v<T> && std::is_same_v<std::remove_extent_t<T>, char>;
/**< Is this is a char-array? */
template <typename T>
concept IsSignedIntegral = std::is_integral_v<T> && std::is_signed_v<T>;
/**< Is this a signed integral type? */
template <typename T>
concept IsUnsignedIntegral =
std::is_integral_v<T> && std::is_unsigned_v<T>;
/**< Is this an unsigned integral type? */
/** /**
@ -158,11 +168,11 @@ namespace Mu::Scm {
*/ */
template<typename T> template<typename T>
SCM make_symbol(const T& val){ SCM make_symbol(const T& val){
using Type = std::remove_const_t<T>; // *not* std::remove_const using Type = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<Type, std::string> || if constexpr (std::is_same_v<Type, std::string> ||
std::is_same_v<Type, std::string_view>) std::is_same_v<Type, std::string_view>)
return scm_from_utf8_symboln(val.data(), val.size()); return scm_from_utf8_symboln(val.data(), val.size());
else if constexpr (is_char_array_v<Type>|| std::is_same_v<Type, const char*>) else if constexpr (IsCharArray<T> || std::is_same_v<Type, const char*>)
return scm_from_utf8_symbol(val); return scm_from_utf8_symbol(val);
else { else {
static_assert(always_false<Type>::value, "source type not supported"); static_assert(always_false<Type>::value, "source type not supported");
@ -184,15 +194,17 @@ namespace Mu::Scm {
*/ */
template<typename T> template<typename T>
T from_scm(SCM ARG, const char *func, int pos) { T from_scm(SCM ARG, const char *func, int pos) {
const auto ensure=[&](bool pred, SCM ARG, const char *expected) { const auto ensure = [&](bool pred, SCM ARG, const char *expected) {
if (!pred) if (!pred)
throw ScmError{ScmError::Id::WrongType, func, pos, ARG, expected}; throw ScmError{ScmError::Id::WrongType, func, pos, ARG, expected};
}; };
// note: use the C predicates (scm_is_string etc.); the Scheme // note: use the C predicates (scm_is_string etc.); the Scheme
// predicates (scm_string_p etc.) return an SCM boolean, which // predicates (scm_string_p etc.) return an SCM boolean, which
// is truthy as a C++ bool even when it is #f. // is truthy as a C++ bool even when it is #f.
using Type = std::remove_const_t<T>; // *not* std::remove_const using Type = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<Type, std::string>) { if constexpr (std::is_same_v<Type, SCM>) {
return ARG;
} else if constexpr (std::is_same_v<Type, std::string>) {
ensure(scm_is_string(ARG), ARG, "string"); ensure(scm_is_string(ARG), ARG, "string");
size_t len{}; size_t len{};
auto str{scm_to_utf8_stringn(ARG, &len)}; auto str{scm_to_utf8_stringn(ARG, &len)};
@ -205,23 +217,23 @@ namespace Mu::Scm {
} else if constexpr (std::is_same_v<Type, bool>) { } else if constexpr (std::is_same_v<Type, bool>) {
ensure(scm_is_bool(ARG), ARG, "bool"); ensure(scm_is_bool(ARG), ARG, "bool");
return scm_to_bool(ARG); return scm_to_bool(ARG);
} else if constexpr (std::is_same_v<Type, int>) { } else if constexpr (IsSignedIntegral<Type>) {
ensure(scm_is_signed_integer(ARG, std::numeric_limits<int>::min(), ensure(scm_is_signed_integer(ARG, std::numeric_limits<Type>::min(),
std::numeric_limits<int>::max()), std::numeric_limits<Type>::max()),
ARG, "integer"); ARG, "integer");
return scm_to_int(ARG); return scm_to_int(ARG);
} else if constexpr (std::is_same_v<Type, uint>) { } else if constexpr (IsUnsignedIntegral<Type>) {
ensure(scm_is_unsigned_integer(ARG, std::numeric_limits<uint>::min(), ensure(scm_is_unsigned_integer(ARG, std::numeric_limits<Type>::min(),
std::numeric_limits<uint>::max()), std::numeric_limits<Type>::max()),
ARG, "unsigned"); ARG, "unsigned");
return scm_to_uint(ARG); return scm_to_uint(ARG);
} else if constexpr (std::is_same_v<Type, SCM>) {
return ARG;
} else { } else {
static_assert(always_false<Type>::value, "target type not supported"); static_assert(always_false<Type>::value, "target type not supported");
return {}; return {};
} }
} }
/** /**
* Like from_SCM, but if ARG is boolean false, return default value. * Like from_SCM, but if ARG is boolean false, return default value.
* *
@ -245,11 +257,11 @@ namespace Mu::Scm {
*/ */
template<typename T> template<typename T>
SCM to_scm(const T& val) { SCM to_scm(const T& val) {
using Type = std::remove_const_t<T>; using Type = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<Type, std::string> || if constexpr (std::is_same_v<Type, std::string> ||
std::is_same_v<Type, std::string_view>) std::is_same_v<Type, std::string_view>)
return scm_from_utf8_stringn(val.data(), val.size()); return scm_from_utf8_stringn(val.data(), val.size());
else if constexpr (is_char_array_v<Type>|| std::is_same_v<Type, const char*>) else if constexpr (IsCharArray<Type>|| std::is_same_v<Type, const char*>)
return scm_from_utf8_string(val); return scm_from_utf8_string(val);
else if constexpr (std::is_same_v<Type, std::vector<std::string>>) { else if constexpr (std::is_same_v<Type, std::vector<std::string>>) {
SCM lst{SCM_EOL}; SCM lst{SCM_EOL};

View File

@ -478,6 +478,11 @@ Example:
(root-maildir . "/home/user/Maildir") (schema-version . 500)) (root-maildir . "/home/user/Maildir") (schema-version . 500))
@end lisp @end lisp
Some of the fields are constant for the lifetime of the store, while others such
as the @code{last-change} may change between calls.
All date fields are encoded as Unix seconds-since-epoch.
More fields may be added. More fields may be added.
@deffn {Scheme Procedure} root-maildir @deffn {Scheme Procedure} root-maildir