From 3a55b8e6fd51c3dda1ea71c6806f2ee2d71c1065 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Tue, 29 Aug 2023 13:15:45 -0500 Subject: notmuch: add notmuch bindings aerc is using an unmaintained fork of a not-well-functioning notmuch binding library. Add custom bindings directly into the aerc repo to make them more maintainable and more customizable to our needs. Signed-off-by: Tim Culverhouse Acked-by: Robin Jarry --- lib/notmuch/message.go | 260 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 lib/notmuch/message.go (limited to 'lib/notmuch/message.go') diff --git a/lib/notmuch/message.go b/lib/notmuch/message.go new file mode 100644 index 00000000..5b97e39f --- /dev/null +++ b/lib/notmuch/message.go @@ -0,0 +1,260 @@ +//go:build notmuch +// +build notmuch + +package notmuch + +/* +#cgo LDFLAGS: -lnotmuch + +#include +#include + +*/ +import "C" + +import ( + "time" + "unsafe" +) + +type Message struct { + message *C.notmuch_message_t +} + +// Close frees resources associated with the message +func (m *Message) Close() { + C.notmuch_message_destroy(m.message) +} + +// ID returns the message ID +func (m *Message) ID() string { + cID := C.notmuch_message_get_message_id(m.message) + return C.GoString(cID) +} + +// ThreadID returns the thread ID of the message +func (m *Message) ThreadID() string { + cID := C.notmuch_message_get_thread_id(m.message) + return C.GoString(cID) +} + +func (m *Message) Replies() Messages { + cMessages := C.notmuch_message_get_replies(m.message) + return Messages{ + messages: cMessages, + } +} + +func (m *Message) TotalFiles() int { + return int(C.notmuch_message_count_files(m.message)) +} + +// Filename returns a single filename associated with the message. If the +// message has multiple filenames, the return value will be arbitrarily chosen +func (m *Message) Filename() string { + cFilename := C.notmuch_message_get_filename(m.message) + return C.GoString(cFilename) +} + +func (m *Message) Filenames() []string { + cFilenames := C.notmuch_message_get_filenames(m.message) + defer C.notmuch_filenames_destroy(cFilenames) + + filenames := []string{} + for C.notmuch_filenames_valid(cFilenames) > 0 { + filename := C.notmuch_filenames_get(cFilenames) + filenames = append(filenames, C.GoString(filename)) + C.notmuch_filenames_move_to_next(cFilenames) + } + return filenames +} + +// TODO is this needed? +// func (m *Message) Reindex() error { +// +// } + +type Flag int + +const ( + MESSAGE_FLAG_MATCH Flag = iota + MESSAGE_FLAG_EXCLUDED + MESSAGE_FLAG_GHOST +) + +func (m *Message) Flag(flag Flag) (bool, error) { + var ok C.notmuch_bool_t + cFlag := C.notmuch_message_flag_t(flag) + err := errorWrap(C.notmuch_message_get_flag_st(m.message, cFlag, &ok)) + if err != nil { + return false, err + } + if ok == 0 { + return false, nil + } + return true, nil +} + +// TODO why does this exist?? +// func (m *Message) SetFlag(flag Flag) { +// +// } + +func (m *Message) Date() time.Time { + cTime := C.notmuch_message_get_date(m.message) + return time.Unix(int64(cTime), 0) +} + +func (m *Message) Header(field string) string { + cField := C.CString(field) + defer C.free(unsafe.Pointer(cField)) + cHeader := C.notmuch_message_get_header(m.message, cField) + return C.GoString(cHeader) +} + +func (m *Message) Tags() []string { + cTags := C.notmuch_message_get_tags(m.message) + defer C.notmuch_tags_destroy(cTags) + + tags := []string{} + for C.notmuch_tags_valid(cTags) > 0 { + tag := C.notmuch_tags_get(cTags) + tags = append(tags, C.GoString(tag)) + C.notmuch_tags_move_to_next(cTags) + } + return tags +} + +func (m *Message) AddTag(tag string) error { + cTag := C.CString(tag) + defer C.free(unsafe.Pointer(cTag)) + + return errorWrap(C.notmuch_message_add_tag(m.message, cTag)) +} + +func (m *Message) RemoveTag(tag string) error { + cTag := C.CString(tag) + defer C.free(unsafe.Pointer(cTag)) + + return errorWrap(C.notmuch_message_remove_tag(m.message, cTag)) +} + +func (m *Message) RemoveAllTags() error { + return errorWrap(C.notmuch_message_remove_all_tags(m.message)) +} + +// SyncTagsToMaildirFlags adds/removes the appropriate tags to the maildir +// filename +func (m *Message) SyncTagsToMaildirFlags() error { + return errorWrap(C.notmuch_message_tags_to_maildir_flags(m.message)) +} + +// SyncMaildirFlagsToTags syncs the current maildir flags to the notmuch tags +func (m *Message) SyncMaildirFlagsToTags() error { + return errorWrap(C.notmuch_message_maildir_flags_to_tags(m.message)) +} + +func (m *Message) HasMaildirFlag(flag rune) (bool, error) { + var ok C.notmuch_bool_t + err := errorWrap(C.notmuch_message_has_maildir_flag_st(m.message, C.char(flag), &ok)) + if err != nil { + return false, err + } + if ok == 0 { + return false, nil + } + return true, nil +} + +func (m *Message) Freeze() error { + return errorWrap(C.notmuch_message_freeze(m.message)) +} + +func (m *Message) Thaw() error { + return errorWrap(C.notmuch_message_thaw(m.message)) +} + +func (m *Message) Property(key string) (string, error) { + var ( + cKey *C.char + cValue *C.char + ) + defer C.free(unsafe.Pointer(cKey)) + defer C.free(unsafe.Pointer(cValue)) + cKey = C.CString(key) + err := errorWrap(C.notmuch_message_get_property(m.message, cKey, &cValue)) //nolint:gocritic // see note in notmuch.go + if err != nil { + return "", err + } + return C.GoString(cValue), nil +} + +func (m *Message) AddProperty(key string, value string) error { + var ( + cKey *C.char + cValue *C.char + ) + defer C.free(unsafe.Pointer(cKey)) + defer C.free(unsafe.Pointer(cValue)) + cKey = C.CString(key) + cValue = C.CString(value) + return errorWrap(C.notmuch_message_add_property(m.message, cKey, cValue)) +} + +func (m *Message) RemoveProperty(key string, value string) error { + var ( + cKey *C.char + cValue *C.char + ) + defer C.free(unsafe.Pointer(cKey)) + defer C.free(unsafe.Pointer(cValue)) + cKey = C.CString(key) + cValue = C.CString(value) + return errorWrap(C.notmuch_message_remove_property(m.message, cKey, cValue)) +} + +func (m *Message) RemoveAllProperties(key string) error { + var cKey *C.char + defer C.free(unsafe.Pointer(cKey)) + cKey = C.CString(key) + return errorWrap(C.notmuch_message_remove_all_properties(m.message, cKey)) +} + +func (m *Message) RemoveAllPropertiesWithPrefix(prefix string) error { + var cPrefix *C.char + defer C.free(unsafe.Pointer(cPrefix)) + cPrefix = C.CString(prefix) + return errorWrap(C.notmuch_message_remove_all_properties_with_prefix(m.message, cPrefix)) +} + +func (m *Message) Properties(key string, exact bool) *Properties { + var ( + cKey *C.char + cExact C.int + ) + defer C.free(unsafe.Pointer(cKey)) + if exact { + cExact = 1 + } + + cKey = C.CString(key) + props := C.notmuch_message_get_properties(m.message, cKey, cExact) + + return &Properties{ + properties: props, + } +} + +func (m *Message) CountProperties(key string) (int, error) { + var ( + cKey *C.char + cCount C.uint + ) + defer C.free(unsafe.Pointer(cKey)) + cKey = C.CString(key) + err := errorWrap(C.notmuch_message_count_properties(m.message, cKey, &cCount)) + if err != nil { + return 0, err + } + return int(cCount), nil +} -- cgit