diff --git a/lib/message/mu-document.cc b/lib/message/mu-document.cc index f0c230c5..3c43aa0f 100644 --- a/lib/message/mu-document.cc +++ b/lib/message/mu-document.cc @@ -19,6 +19,7 @@ #include "mu-document.hh" #include "mu-message.hh" +#include "utils/mu-sexp.hh" #include #include @@ -30,11 +31,37 @@ #include #include + using namespace Mu; constexpr uint8_t SepaChar1 = 0xfe; constexpr uint8_t SepaChar2 = 0xff; + +const Xapian::Document& +Document::xapian_document() const +{ + if (dirty_sexp_) { + xdoc_.set_data(sexp_.to_string()); + dirty_sexp_ = false; + } + return xdoc_; +} + +template void +Document::put_prop(const std::string& pname, SexpType&& val) +{ + sexp_.put_props(pname, std::forward(val)); + dirty_sexp_ = true; +} + +template void +Document::put_prop(const Field& field, SexpType&& val) +{ + put_prop(std::string(":") + std::string{field.name}, + std::forward(val)); +} + static void add_search_term(Xapian::Document& doc, const Field& field, const std::string& val) { @@ -57,12 +84,6 @@ add_search_term(Xapian::Document& doc, const Field& field, const std::string& va } -static std::string -make_prop_name(const Field& field) -{ - return ":" + std::string(field.name); -} - void Document::add(Field::Id id, const std::string& val) { @@ -74,9 +95,9 @@ Document::add(Field::Id id, const std::string& val) if (field.is_searchable()) add_search_term(xdoc_, field, val); - if (field.include_in_sexp()) - sexp_list().add_prop(make_prop_name(field), - Sexp::make_string(std::move(val))); + if (field.include_in_sexp()) { + put_prop(field, val); + } } void @@ -95,11 +116,10 @@ Document::add(Field::Id id, const std::vector& vals) add_search_term(xdoc_, field, val); }); if (field.include_in_sexp()) { - Sexp::List elms; + Sexp elms{}; for(auto&& val: vals) - elms.add(Sexp::make_string(val)); - sexp_list().add_prop(make_prop_name(field), - Sexp::make_list(std::move(elms))); + elms.add(val); + put_prop(field, std::move(elms)); } } @@ -113,19 +133,16 @@ Document::string_vec_value(Field::Id field_id) const noexcept static Sexp make_contacts_sexp(const Contacts& contacts) { - Sexp::List clist; + Sexp contacts_sexp; seq_for_each(contacts, [&](auto&& c) { + Sexp contact(":email"_sym, c.email); if (!c.name.empty()) - clist.add(Sexp::make_prop_list( - ":name", Sexp::make_string(c.name), - ":email", Sexp::make_string(c.email))); - else - clist.add(Sexp::make_prop_list( - ":email", Sexp::make_string(c.email))); + contact.add(":name"_sym, c.name); + contacts_sexp.add(std::move(contact)); }); - return Sexp::make_list(std::move(clist)); + return contacts_sexp; } void @@ -168,9 +185,7 @@ Document::add(Field::Id id, const Contacts& contacts) xdoc_.add_value(field.value_no(), join(cvec, SepaChar1)); if (field.include_in_sexp()) - sexp_list().add_prop(make_prop_name(field), - make_contacts_sexp(contacts)); - + put_prop(field, make_contacts_sexp(contacts)); } Contacts @@ -204,22 +219,19 @@ Document::contacts_value(Field::Id id) const noexcept void Document::add_extra_contacts(const std::string& propname, const Contacts& contacts) { - if (!contacts.empty()) - sexp_list().add_prop(std::string{propname}, - make_contacts_sexp(contacts)); + if (!contacts.empty()) { + put_prop(propname, make_contacts_sexp(contacts)); + dirty_sexp_ = true; + } } static Sexp make_emacs_time_sexp(::time_t t) { - Sexp::List dlist; - - dlist.add(Sexp::make_number(static_cast(t >> 16))); - dlist.add(Sexp::make_number(static_cast(t & 0xffff))); - dlist.add(Sexp::make_number(0)); - - return Sexp::make_list(std::move(dlist)); + return Sexp().add(static_cast(t >> 16), + static_cast(t & 0xffff), + 0); } void @@ -231,7 +243,6 @@ Document::add(Field::Id id, int64_t val) * we comply, by storing a number a base-16 and prefixing with 'f' + * length; such that the strings are sorted in the numerical order. */ - const auto field{field_from_id(id)}; if (field.is_value()) @@ -239,11 +250,9 @@ Document::add(Field::Id id, int64_t val) if (field.include_in_sexp()) { if (field.is_time_t()) - sexp_list().add_prop(make_prop_name(field), - make_emacs_time_sexp(val)); + put_prop(field, make_emacs_time_sexp(val)); else - sexp_list().add_prop(make_prop_name(field), - Sexp::make_number(val)); + put_prop(field, val); } } @@ -265,8 +274,7 @@ Document::add(Priority prio) xdoc_.add_boolean_term(field.xapian_term(to_char(prio))); if (field.include_in_sexp()) - sexp_list().add_prop(make_prop_name(field), - Sexp::make_symbol_sv(priority_name(prio))); + put_prop(field, Sexp::Symbol(priority_name(prio))); } Priority @@ -281,52 +289,21 @@ Document::add(Flags flags) { constexpr auto field{field_from_id(Field::Id::Flags)}; - Sexp::List flaglist; + Sexp flaglist; xdoc_.add_value(field.value_no(), to_lexnum(static_cast(flags))); flag_infos_for_each([&](auto&& flag_info) { auto term=[&](){return field.xapian_term(flag_info.shortcut_lower());}; if (any_of(flag_info.flag & flags)) { xdoc_.add_boolean_term(term()); - flaglist.add(Sexp::make_symbol_sv(flag_info.name)); + flaglist.add(Sexp::Symbol(flag_info.name)); } }); if (field.include_in_sexp()) - sexp_list().add_prop(make_prop_name(field), - Sexp::make_list(std::move(flaglist))); + put_prop(field, std::move(flaglist)); } -Sexp::List& -Document::sexp_list() -{ - /* perhaps we need get the sexp_ from the document first? */ - if (sexp_list_.empty()) { - const auto str{xdoc_.get_data()}; - if (!str.empty()) { - Sexp sexp{Sexp::make_parse(str)}; - sexp_list_ = sexp.list(); - } - } - - return sexp_list_; -} - -std::string -Document::cached_sexp() const -{ - return xdoc_.get_data(); -} - -void -Document::update_cached_sexp(void) -{ - if (sexp_list_.empty()) - return; /* nothing to do; i.e. the exisiting sexp is still up to - * date */ - xdoc_.set_data(Sexp::make_list(Sexp::List{sexp_list()}).to_sexp_string()); -} - Flags Document::flags_value() const noexcept { @@ -364,7 +341,6 @@ Document::remove(Field::Id field_id) } } }); - } diff --git a/lib/message/mu-document.hh b/lib/message/mu-document.hh index 9c923a19..4cfe2442 100644 --- a/lib/message/mu-document.hh +++ b/lib/message/mu-document.hh @@ -52,13 +52,23 @@ public: * * @param doc */ - Document(const Xapian::Document& doc): xdoc_{doc} {} + Document(const Xapian::Document& doc): xdoc_{doc} { + if (auto&& s{Sexp::parse(xdoc_.get_data())}; s) + sexp_ = std::move(*s); + } + + /** + * DTOR + */ + ~Document() { + xapian_document(); // for side-effect up updating sexp. + } /** * Get a reference to the underlying Xapian document. * */ - const Xapian::Document& xapian_document() const { return xdoc_; } + const Xapian::Document& xapian_document() const; /** * Get the doc-id for this document @@ -138,24 +148,12 @@ public: void remove(Field::Id field_id); /** - * Update the cached sexp from the sexp_list_ - */ - void update_cached_sexp(); - - /** - * Get the cached s-expression - * - * @return a string - */ - std::string cached_sexp() const; - - /** - * Get the cached s-expressionl useful for changing + * Get the cached s-expression useful for changing * it (call update_sexp_cache() when done) * - * @return the cache s-expression + * @return the cached s-expression */ - Sexp::List& sexp_list(); + const Sexp& sexp() const { return sexp_; } /** * Generically adds an optional value, if set, to the document @@ -184,6 +182,7 @@ public: return xdoc_.get_value(field_from_id(field_id).value_no()); }, std::string{}); } + /** * Get a vec of string values. * @@ -229,9 +228,13 @@ public: Flags flags_value() const noexcept; private: - Xapian::Document xdoc_; - Sexp::List sexp_list_; + template void put_prop(const Field& field, SexpType&& val); + template void put_prop(const std::string& pname, SexpType&& val); + + mutable Xapian::Document xdoc_; + Sexp sexp_; + mutable bool dirty_sexp_{}; /* xdoc's sexp is outdated */ }; } // namepace Mu diff --git a/lib/message/mu-fields.hh b/lib/message/mu-fields.hh index 22b486b5..2579a767 100644 --- a/lib/message/mu-fields.hh +++ b/lib/message/mu-fields.hh @@ -186,8 +186,8 @@ struct Field { * */ - constexpr char xapian_prefix() const - { /* xapian uses uppercase shortcuts; toupper is not constexpr */ + constexpr char xapian_prefix() const { + /* xapian uses uppercase shortcuts; toupper is not constexpr */ return shortcut == 0 ? 0 : shortcut - ('a' - 'A'); } @@ -542,5 +542,6 @@ Option field_from_number(size_t id) return field_from_id(static_cast(id)); } + } // namespace Mu #endif /* MU_FIELDS_HH__ */ diff --git a/lib/message/mu-message.cc b/lib/message/mu-message.cc index a8ff0f5f..58102609 100644 --- a/lib/message/mu-message.cc +++ b/lib/message/mu-message.cc @@ -182,23 +182,16 @@ Message::docid() const } -const Mu::Sexp::List& -Message::to_sexp_list() const +const Mu::Sexp& +Message::sexp() const { - return priv_->doc.sexp_list(); -} - -void -Message::update_cached_sexp() -{ - priv_->doc.update_cached_sexp(); + return priv_->doc.sexp(); } Result Message::set_maildir(const std::string& maildir) { /* sanity check a little bit */ - if (maildir.empty() || maildir.at(0) != '/' || (maildir.size() > 1 && maildir.at(maildir.length()-1) == '/')) diff --git a/lib/message/mu-message.hh b/lib/message/mu-message.hh index 3f2e001b..615b5d8c 100644 --- a/lib/message/mu-message.hh +++ b/lib/message/mu-message.hh @@ -23,6 +23,8 @@ #include #include #include +#include + #include "mu-contact.hh" #include "mu-priority.hh" #include "mu-flags.hh" @@ -336,37 +338,17 @@ public: .string_vec_value(Field::Id::Tags); } - /** - * Get the cached s-expression for this message, or {} if not available. - * - * @return sexp or empty. - */ - std::string cached_sexp() const { - return document().cached_sexp(); - } - /* * Convert to Sexp */ /** - * Get the s-expression for this message. Stays valid as long - * as this message is. + * Get the s-expression for this message. Stays valid as long as this + * message is. * - * @return a Mu::Sexp::List representing the message. + * @return an Sexp representing the message. */ - const Mu::Sexp::List& to_sexp_list() const; - Mu::Sexp to_sexp() const { - return Sexp::make_list(Sexp::List(to_sexp_list())); - } - - /** - * Update the cached sexp for this message which is stored in the - * document. This should be done immediately before storing it in the - * database. - * - */ - void update_cached_sexp(); + const Sexp& sexp() const; /* * And some non-const message, for updating an existing @@ -477,5 +459,15 @@ private: }; // Message MU_ENABLE_BITOPS(Message::Options); + + +static inline std::ostream& +operator<<(std::ostream& os, const Message& msg) +{ + os << msg.sexp(); + return os; +} + + } // Mu #endif /* MU_MESSAGE_HH__ */ diff --git a/lib/message/test-mu-message.cc b/lib/message/test-mu-message.cc index a96b44bf..9cfc1a4d 100644 --- a/lib/message/test-mu-message.cc +++ b/lib/message/test-mu-message.cc @@ -481,8 +481,7 @@ Content-Type: message/rfc822 )"; auto message{Message::make_from_text(msgtext)}; g_assert_true(!!message); - - g_assert_true(message->cached_sexp().empty()); + //g_assert_true(message->sexp().empty()); }