~rockorager/offmap

offmap: RFC: Package restructuring v1 PROPOSED

A first draft if the repo restructuring

Just does the small and easy things for now :)

Moritz Poldrack (3):
  refactor: move config into it's own package
  refactor: move state into it's own package
  refactor: move main package to project root

 Makefile                             |   2 +-
 cmd/diff.go                          |  61 --------
 config.go => config/config.go        |   2 +-
 diff.go                              | 218 ++++++---------------------
 imap/diff.go                         |  20 +--
 imap/imap.go                         |  31 ++--
 imap/update.go                       |   4 +-
 maildir/diff.go                      |  18 +--
 maildir/maildir.go                   |  25 +--
 maildir/maildir_test.go              |   6 +-
 maildir/update.go                    |  14 +-
 maildir/update_test.go               |  33 ++--
 cmd/offmap.go => offmap.go           |   0
 state/diff.go                        | 194 ++++++++++++++++++++++++
 diff_test.go => state/diff_test.go   |   2 +-
 email.go => state/email.go           |   2 +-
 mailbox.go => state/mailbox.go       |   2 +-
 state.go => state/state.go           |   7 +-
 state_test.go => state/state_test.go |   7 +-
 cmd/sync.go => sync.go               |   7 +-
 20 files changed, 331 insertions(+), 324 deletions(-)
 delete mode 100644 cmd/diff.go
 rename config.go => config/config.go (99%)
 rename cmd/offmap.go => offmap.go (100%)
 create mode 100644 state/diff.go
 rename diff_test.go => state/diff_test.go (99%)
 rename email.go => state/email.go (98%)
 rename mailbox.go => state/mailbox.go (98%)
 rename state.go => state/state.go (96%)
 rename state_test.go => state/state_test.go (88%)
 rename cmd/sync.go => sync.go (93%)

-- 
2.38.1
#897634 .build.yml failed
offmap/patches/.build.yml: FAILED in 1m49s

[RFC: Package restructuring][0] from [Moritz Poldrack][1]

[0]: https://lists.sr.ht/~rockorager/offmap/patches/37335
[1]: mailto:git@moritz.sh

✗ #897634 FAILED offmap/patches/.build.yml https://builds.sr.ht/~rockorager/job/897634
I would personally have preferred to also split this up a bit, but then
I'd have to resolve the circular dependecies and I really don't feel
like burdening myself with that :D
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rockorager/offmap/patches/37335/mbox | git am -3
Learn more about email & git

[PATCH offmap 1/3] refactor: move config into it's own package Export this patch

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 cmd/diff.go                   |  3 ++-
 cmd/sync.go                   |  3 ++-
 config.go => config/config.go |  2 +-
 imap/imap.go                  |  5 +++--
 maildir/maildir.go            |  3 ++-
 maildir/maildir_test.go       |  6 +++---
 maildir/update_test.go        | 13 +++++++------
 state.go                      |  3 ++-
 state_test.go                 |  3 ++-
 9 files changed, 24 insertions(+), 17 deletions(-)
 rename config.go => config/config.go (99%)

diff --git a/cmd/diff.go b/cmd/diff.go
index 6855bf4..c400d1c 100644
--- a/cmd/diff.go
+++ b/cmd/diff.go
@@ -4,6 +4,7 @@ import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/maildir"
@@ -22,7 +23,7 @@ func newDiffCommand() *cobra.Command {
func diff(cmd *cobra.Command, args []string) {
	verbosity, _ := cmd.Flags().GetCount("verbose")
	log.SetLevel(verbosity)
	cfgs, err := offmap.LoadConfig()
	cfgs, err := config.LoadConfig()
	if err != nil {
		log.Fatalf("could not read config: %v", err)
	}
diff --git a/cmd/sync.go b/cmd/sync.go
index 4568077..2004c79 100644
--- a/cmd/sync.go
+++ b/cmd/sync.go
@@ -4,6 +4,7 @@ import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/maildir"
@@ -22,7 +23,7 @@ func newSyncCommand() *cobra.Command {
func sync(cmd *cobra.Command, args []string) {
	verbosity, _ := cmd.Flags().GetCount("verbose")
	log.SetLevel(verbosity)
	cfgs, err := offmap.LoadConfig()
	cfgs, err := config.LoadConfig()
	if err != nil {
		log.Debugf("%v", err)
	}
diff --git a/config.go b/config/config.go
similarity index 99%
rename from config.go
rename to config/config.go
index cf7fedc..c2af775 100644
--- a/config.go
+++ b/config/config.go
@@ -1,4 +1,4 @@
package offmap
package config

import (
	"fmt"
diff --git a/imap/imap.go b/imap/imap.go
index 2e21e78..fb860c6 100644
--- a/imap/imap.go
+++ b/imap/imap.go
@@ -12,6 +12,7 @@ import (
	"sync/atomic"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap/condstore"
	"git.sr.ht/~rockorager/offmap/log"
	"github.com/emersion/go-imap"
@@ -20,7 +21,7 @@ import (
// Store is a mailstore with an IMAP backend
type Store struct {
	sync.Mutex
	cfg     *offmap.Config
	cfg     *config.Config
	clients map[int]*conn
	// delim is the imap server's hierarchical delimiter. Offmap uses "/",
	// so a server which uses "." must have the "."s replaced
@@ -38,7 +39,7 @@ type Store struct {
}

// NewStore creates a new IMAP store
func NewStore(cfg *offmap.Config) (*Store, error) {
func NewStore(cfg *config.Config) (*Store, error) {
	store := &Store{cfg: cfg}

	if cfg.PasswordCmd != "" {
diff --git a/maildir/maildir.go b/maildir/maildir.go
index 39c7b10..306edbe 100644
--- a/maildir/maildir.go
+++ b/maildir/maildir.go
@@ -11,6 +11,7 @@ import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/log"
	"github.com/emersion/go-maildir"
)
@@ -23,7 +24,7 @@ type Store struct {
	mailboxes map[string]*offmap.Mailbox
}

func NewStore(cfg *offmap.Config) (*Store, error) {
func NewStore(cfg *config.Config) (*Store, error) {
	store := &Store{}

	store.root = cfg.Maildir
diff --git a/maildir/maildir_test.go b/maildir/maildir_test.go
index 35b8fad..a5a0f7b 100644
--- a/maildir/maildir_test.go
+++ b/maildir/maildir_test.go
@@ -4,7 +4,7 @@ import (
	"path"
	"testing"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"github.com/emersion/go-maildir"
	"github.com/stretchr/testify/assert"
)
@@ -44,7 +44,7 @@ func initMaildir(t *testing.T) string {
}

func TestNewStore(t *testing.T) {
	cfg := &offmap.Config{}
	cfg := &config.Config{}

	t.Run("NoConfig", func(t *testing.T) {
		_, err := NewStore(cfg)
@@ -80,7 +80,7 @@ func TestParseFilename(t *testing.T) {

func TestReadEmailState(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
diff --git a/maildir/update_test.go b/maildir/update_test.go
index b29a891..ae0be20 100644
--- a/maildir/update_test.go
+++ b/maildir/update_test.go
@@ -6,13 +6,14 @@ import (
	"testing"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"github.com/emersion/go-maildir"
	"github.com/stretchr/testify/assert"
)

func TestCreateMaildir(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
@@ -29,7 +30,7 @@ func TestCreateMaildir(t *testing.T) {

func TestRenameMaildir(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
@@ -61,7 +62,7 @@ func TestRenameMaildir(t *testing.T) {

func TestCreateEmail(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
@@ -93,7 +94,7 @@ func TestCreateEmail(t *testing.T) {

func TestAddFlags(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
@@ -115,7 +116,7 @@ func TestAddFlags(t *testing.T) {

func TestRemoveFlags(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
@@ -137,7 +138,7 @@ func TestRemoveFlags(t *testing.T) {

func TestDeleteEmails(t *testing.T) {
	root := initMaildir(t)
	cfg := &offmap.Config{Maildir: root}
	cfg := &config.Config{Maildir: root}

	store, err := NewStore(cfg)
	assert.NoError(t, err)
diff --git a/state.go b/state.go
index 7d9c23c..dbed53a 100644
--- a/state.go
+++ b/state.go
@@ -7,6 +7,7 @@ import (
	"path"
	"time"

	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/log"
	"github.com/emersion/go-maildir"
	"github.com/spf13/viper"
@@ -26,7 +27,7 @@ type State struct {
}

// NewState creates a new application state and validates the config
func NewState(cfg *Config) (*State, error) {
func NewState(cfg *config.Config) (*State, error) {
	root := cfg.Maildir
	if root == "" {
		return nil, fmt.Errorf("offmap: no maildir path set")
diff --git a/state_test.go b/state_test.go
index d83d945..6c8734f 100644
--- a/state_test.go
+++ b/state_test.go
@@ -4,12 +4,13 @@ import (
	"fmt"
	"testing"

	"git.sr.ht/~rockorager/offmap/config"
	"github.com/stretchr/testify/assert"
)

func TestState(t *testing.T) {
	tmp := t.TempDir()
	cfg := &Config{
	cfg := &config.Config{
		Maildir: tmp,
	}
	state, err := NewState(cfg)
-- 
2.38.1

[PATCH offmap 2/3] refactor: move state into it's own package Export this patch

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 cmd/diff.go                          |  4 ++--
 cmd/sync.go                          |  4 ++--
 imap/diff.go                         | 20 ++++++++++----------
 imap/imap.go                         | 26 +++++++++++++-------------
 imap/update.go                       |  4 ++--
 maildir/diff.go                      | 18 +++++++++---------
 maildir/maildir.go                   | 22 +++++++++++-----------
 maildir/update.go                    | 14 +++++++-------
 maildir/update_test.go               | 20 ++++++++++----------
 diff.go => state/diff.go             |  2 +-
 diff_test.go => state/diff_test.go   |  2 +-
 email.go => state/email.go           |  2 +-
 mailbox.go => state/mailbox.go       |  2 +-
 state.go => state/state.go           |  6 +++---
 state_test.go => state/state_test.go |  4 ++--
 15 files changed, 75 insertions(+), 75 deletions(-)
 rename diff.go => state/diff.go (99%)
 rename diff_test.go => state/diff_test.go (99%)
 rename email.go => state/email.go (98%)
 rename mailbox.go => state/mailbox.go (98%)
 rename state.go => state/state.go (97%)
 rename state_test.go => state/state_test.go (94%)

diff --git a/cmd/diff.go b/cmd/diff.go
index c400d1c..fef7f5d 100644
--- a/cmd/diff.go
+++ b/cmd/diff.go
@@ -3,11 +3,11 @@ package main
import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/maildir"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/spf13/cobra"
)

@@ -31,7 +31,7 @@ func diff(cmd *cobra.Command, args []string) {
		start := time.Now()

		// Bootstrap the app
		state, err := offmap.NewState(cfg)
		state, err := state.New(cfg)
		if err != nil {
			log.Fatalf("%v", err)
		}
diff --git a/cmd/sync.go b/cmd/sync.go
index 2004c79..b0e5e49 100644
--- a/cmd/sync.go
+++ b/cmd/sync.go
@@ -3,11 +3,11 @@ package main
import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/maildir"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/spf13/cobra"
)

@@ -32,7 +32,7 @@ func sync(cmd *cobra.Command, args []string) {
		start := time.Now()

		// Bootstrap the app
		state, err := offmap.NewState(cfg)
		state, err := state.New(cfg)
		if err != nil {
			log.Fatalf("%v", err)
		}
diff --git a/imap/diff.go b/imap/diff.go
index c7de4bd..1f7b2b6 100644
--- a/imap/diff.go
+++ b/imap/diff.go
@@ -4,23 +4,23 @@ import (
	"fmt"
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/state"
)

func (s *Store) Diff(cached []*offmap.Mailbox) (*offmap.Diff, error) {
func (s *Store) Diff(cached []*state.Mailbox) (*state.Diff, error) {
	start := time.Now()
	for _, mbox := range cached {
		s.cache[mbox.UIDValidity] = mbox
	}
	diff := offmap.NewDiff()
	diff := state.NewDiff()
	mboxes, err := s.getCurrentState()
	if err != nil {
		return nil, err
	}
	oldMboxes := mapMailboxes(cached)
	newMboxes := mapMailboxes(mboxes)
	diff.Mailboxes = offmap.CompareMailboxes(oldMboxes, newMboxes)
	diff.Mailboxes = state.CompareMailboxes(oldMboxes, newMboxes)

	for dir, mbox := range newMboxes {
		if mbox.Synched {
@@ -29,11 +29,11 @@ func (s *Store) Diff(cached []*offmap.Mailbox) (*offmap.Diff, error) {
		newEmails := mapEmails(mbox.Emails)
		oldMbox, ok := oldMboxes[mbox.LocalName]
		if !ok {
			diff.Emails[dir] = offmap.CompareEmails(nil, newEmails)
			diff.Emails[dir] = state.CompareEmails(nil, newEmails)
			continue
		}
		oldEmails := mapEmails(oldMbox.Emails)
		diff.Emails[dir] = offmap.CompareEmails(oldEmails, newEmails)
		diff.Emails[dir] = state.CompareEmails(oldEmails, newEmails)
	}

	log.Debugf("imap: mailboxes: {%d created, %d renamed, %d deleted}",
@@ -53,16 +53,16 @@ func (s *Store) Diff(cached []*offmap.Mailbox) (*offmap.Diff, error) {
	return diff, nil
}

func mapMailboxes(mboxes []*offmap.Mailbox) map[string]*offmap.Mailbox {
	m := make(map[string]*offmap.Mailbox)
func mapMailboxes(mboxes []*state.Mailbox) map[string]*state.Mailbox {
	m := make(map[string]*state.Mailbox)
	for _, mbox := range mboxes {
		m[mbox.LocalName] = mbox
	}
	return m
}

func mapEmails(emls []*offmap.Email) map[string]*offmap.Email {
	m := make(map[string]*offmap.Email, len(emls))
func mapEmails(emls []*state.Email) map[string]*state.Email {
	m := make(map[string]*state.Email, len(emls))
	for _, eml := range emls {
		m[fmt.Sprintf("%d", eml.UID)] = eml
	}
diff --git a/imap/imap.go b/imap/imap.go
index fb860c6..f2371a4 100644
--- a/imap/imap.go
+++ b/imap/imap.go
@@ -11,10 +11,10 @@ import (
	"sync"
	"sync/atomic"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/imap/condstore"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/emersion/go-imap"
)

@@ -28,9 +28,9 @@ type Store struct {
	delim string
	// tmp is the temp directory where files are located when downloaded
	tmp   string
	cache map[uint32]*offmap.Mailbox
	cache map[uint32]*state.Mailbox

	mailboxes []*offmap.Mailbox
	mailboxes []*state.Mailbox

	// capabilities
	liststatus bool
@@ -55,7 +55,7 @@ func NewStore(cfg *config.Config) (*Store, error) {
		cfg.Password = lines[0]
	}

	store.cache = make(map[uint32]*offmap.Mailbox)
	store.cache = make(map[uint32]*state.Mailbox)
	store.clients = make(map[int]*conn, store.cfg.MaxConnections)
	store.initClients()

@@ -82,7 +82,7 @@ func (s *Store) SupportListStatus() bool {
	return s.liststatus
}

func (s *Store) getCurrentState() ([]*offmap.Mailbox, error) {
func (s *Store) getCurrentState() ([]*state.Mailbox, error) {
	mailboxes, err := s.listMailboxes()
	if err != nil {
		return nil, err
@@ -92,7 +92,7 @@ func (s *Store) getCurrentState() ([]*offmap.Mailbox, error) {
	for _, mbox := range mailboxes {
		client := s.getConn()
		wg.Add(1)
		go func(client *conn, mbox *offmap.Mailbox) {
		go func(client *conn, mbox *state.Mailbox) {
			switch {
			case s.SupportCondstore():
				cached, ok := s.cache[mbox.UIDValidity]
@@ -129,7 +129,7 @@ func (s *Store) getCurrentState() ([]*offmap.Mailbox, error) {
			go func() {
				for msg := range msgCh {
					flags := flagIMAPToMaildir(msg.Flags)
					eml := offmap.NewEmail(mbox, msg.Uid, "", flags)
					eml := state.NewEmail(mbox, msg.Uid, "", flags)
					mbox.Emails = append(mbox.Emails, eml)
					atomic.AddInt32(&emails, 1)
				}
@@ -154,16 +154,16 @@ func (s *Store) getCurrentState() ([]*offmap.Mailbox, error) {
	return mailboxes, nil
}

func (s *Store) DownloadEmail(emls []*offmap.Email) []*offmap.Email {
func (s *Store) DownloadEmail(emls []*state.Email) []*state.Email {
	// TODO redo this batching part, it's not needed like this anymore
	// Batch is a map of mailbox name to eml UID
	batch := map[string][]*offmap.Email{}
	emlMap := make(map[string]*offmap.Email, len(emls))
	batch := map[string][]*state.Email{}
	emlMap := make(map[string]*state.Email, len(emls))
	for _, eml := range emls {
		_, ok := batch[eml.Mailbox.RemoteName]
		if !ok {
			// We haven't seen this mailbox yet
			batch[eml.Mailbox.RemoteName] = []*offmap.Email{}
			batch[eml.Mailbox.RemoteName] = []*state.Email{}
		}
		id := fmt.Sprintf("%d.%d", eml.Mailbox.UIDValidity, eml.UID)
		emlMap[id] = eml
@@ -242,7 +242,7 @@ func (s *Store) DownloadEmail(emls []*offmap.Email) []*offmap.Email {

// listMailboxes fetches mailbox statuses from the IMAP server. If the server
// supports LIST-STATUS, this will be used
func (s *Store) listMailboxes() ([]*offmap.Mailbox, error) {
func (s *Store) listMailboxes() ([]*state.Mailbox, error) {
	var (
		statuses []*imap.MailboxStatus
		ch       = make(chan *imap.MailboxInfo)
@@ -315,7 +315,7 @@ func (s *Store) listMailboxes() ([]*offmap.Mailbox, error) {
		if s.Delimiter() != "/" {
			local = strings.ReplaceAll(local, s.Delimiter(), "/")
		}
		mailbox := offmap.NewMailbox(status.UidValidity, local, status.Name)
		mailbox := state.NewMailbox(status.UidValidity, local, status.Name)
		mailbox.UIDNext = status.UidNext
		if s.SupportCondstore() {
			mailbox.HighestModSeq = parseHighestModSeq(status)
diff --git a/imap/update.go b/imap/update.go
index 852849b..1835584 100644
--- a/imap/update.go
+++ b/imap/update.go
@@ -5,7 +5,7 @@ import (
	"strings"
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/emersion/go-imap"
	uidplus "github.com/emersion/go-imap-uidplus"
	"github.com/emersion/go-maildir"
@@ -13,7 +13,7 @@ import (

// ApplyDiff applies the changes from offmap, updates the internal state, and saves
// the new state to disk.
func (s *Store) ApplyDiff(state *offmap.State, c *offmap.Diff) error {
func (s *Store) ApplyDiff(state *state.State, c *state.Diff) error {
	// Apply changes
	for _, mbox := range c.Mailboxes.Created {
		conn := s.getConn()
diff --git a/maildir/diff.go b/maildir/diff.go
index c792135..221a3ae 100644
--- a/maildir/diff.go
+++ b/maildir/diff.go
@@ -3,34 +3,34 @@ package maildir
import (
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/state"
)

func (s *Store) Diff(old []*offmap.Mailbox) (*offmap.Diff, error) {
func (s *Store) Diff(old []*state.Mailbox) (*state.Diff, error) {
	start := time.Now()
	_, err := s.State()
	if err != nil {
		return nil, err
	}
	oldMboxMap := make(map[string]*offmap.Mailbox, len(old))
	oldMboxMap := make(map[string]*state.Mailbox, len(old))
	for _, m := range old {
		oldMboxMap[m.LocalName] = m
	}
	mboxDiff := offmap.CompareMailboxes(oldMboxMap, s.mailboxes)
	mboxDiff := state.CompareMailboxes(oldMboxMap, s.mailboxes)

	diff := offmap.NewDiff()
	diff := state.NewDiff()
	diff.Mailboxes = mboxDiff

	for dir, mbox := range s.mailboxes {
		newEmails := mapEmails(mbox.Emails)
		oldMbox, ok := oldMboxMap[mbox.LocalName]
		if !ok {
			diff.Emails[dir] = offmap.CompareEmails(nil, newEmails)
			diff.Emails[dir] = state.CompareEmails(nil, newEmails)
			continue
		}
		oldEmails := mapEmails(oldMbox.Emails)
		diff.Emails[dir] = offmap.CompareEmails(oldEmails, newEmails)
		diff.Emails[dir] = state.CompareEmails(oldEmails, newEmails)
	}
	log.Debugf("maildir: mailboxes: {%d created, %d renamed, %d deleted}",
		len(diff.Mailboxes.Created),
@@ -50,8 +50,8 @@ func (s *Store) Diff(old []*offmap.Mailbox) (*offmap.Diff, error) {
	return diff, nil
}

func mapEmails(emls []*offmap.Email) map[string]*offmap.Email {
	m := make(map[string]*offmap.Email, len(emls))
func mapEmails(emls []*state.Email) map[string]*state.Email {
	m := make(map[string]*state.Email, len(emls))
	for _, eml := range emls {
		m[eml.Key] = eml
	}
diff --git a/maildir/maildir.go b/maildir/maildir.go
index 306edbe..51305b3 100644
--- a/maildir/maildir.go
+++ b/maildir/maildir.go
@@ -10,18 +10,18 @@ import (
	"strings"
	"time"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/emersion/go-maildir"
)

// Store represents a maildir storage backend
type Store struct {
	root  string
	state *offmap.State
	state *state.State
	// map of relpath to mailbox
	mailboxes map[string]*offmap.Mailbox
	mailboxes map[string]*state.Mailbox
}

func NewStore(cfg *config.Config) (*Store, error) {
@@ -41,7 +41,7 @@ func NewStore(cfg *config.Config) (*Store, error) {
}

// State returns a map of mailbox name to mailbox object
func (s *Store) State() (map[string]*offmap.Mailbox, error) {
func (s *Store) State() (map[string]*state.Mailbox, error) {
	start := time.Now()
	var err error
	s.mailboxes, err = s.readMailboxState()
@@ -58,7 +58,7 @@ func (s *Store) State() (map[string]*offmap.Mailbox, error) {
}

// DownloadEmail populates the Email.body field
func (s *Store) DownloadEmail(emls []*offmap.Email) []*offmap.Email {
func (s *Store) DownloadEmail(emls []*state.Email) []*state.Email {
	for _, eml := range emls {
		dir := maildir.Dir(path.Join(s.root, eml.Mailbox.LocalName))
		filename, err := dir.Filename(eml.Key)
@@ -81,7 +81,7 @@ func (s *Store) DownloadEmail(emls []*offmap.Email) []*offmap.Email {
// readCurrentState scans the root directory for the current state
// TODO this is only used in tests,
// TODO too many state functions calling each other, fix that
func (s *Store) readCurrentState() (map[string]*offmap.Mailbox, error) {
func (s *Store) readCurrentState() (map[string]*state.Mailbox, error) {
	log.Debugf("maildir: reading current state")
	mailboxes, err := s.readMailboxState()
	if err != nil {
@@ -105,7 +105,7 @@ func (s *Store) readCurrentState() (map[string]*offmap.Mailbox, error) {
//
// The returned map is a map of the local maildir name, as discovered in the
// current state
func (s *Store) readMailboxState() (map[string]*offmap.Mailbox, error) {
func (s *Store) readMailboxState() (map[string]*state.Mailbox, error) {
	var dirs []string
	err := filepath.WalkDir(s.root, func(path string, info fs.DirEntry, err error) error {
		if err != nil {
@@ -141,7 +141,7 @@ func (s *Store) readMailboxState() (map[string]*offmap.Mailbox, error) {
		return nil, err
	}

	mailboxes := make(map[string]*offmap.Mailbox)
	mailboxes := make(map[string]*state.Mailbox)
	for _, mailbox := range dirs {
		// Read the state of the mailbox
		sp := path.Join(s.root, mailbox, ".offmap")
@@ -159,7 +159,7 @@ func (s *Store) readMailboxState() (map[string]*offmap.Mailbox, error) {
				return nil, fmt.Errorf("maildir: could not read state file: %v", err)
			}
		}
		mbox := offmap.NewMailbox(uint32(uidv), mailbox, "")
		mbox := state.NewMailbox(uint32(uidv), mailbox, "")
		s.readEmailState(mbox)
		mailboxes[mailbox] = mbox
	}
@@ -169,7 +169,7 @@ func (s *Store) readMailboxState() (map[string]*offmap.Mailbox, error) {

// readEmailState reads the current state of emails within the maildir. Messages
// are moved from new to cur in this step
func (s *Store) readEmailState(mbox *offmap.Mailbox) error {
func (s *Store) readEmailState(mbox *state.Mailbox) error {
	dir := maildir.Dir(path.Join(s.root, mbox.LocalName))
	// Move files from new to cur
	_, err := dir.Unseen()
@@ -190,7 +190,7 @@ func (s *Store) readEmailState(mbox *offmap.Mailbox) error {
		file := filepath.Base(path)
		key, flags, err := parseFilename(file)

		eml := offmap.NewEmail(mbox, 0, key, flags)
		eml := state.NewEmail(mbox, 0, key, flags)
		mbox.Emails = append(mbox.Emails, eml)
		return nil
	})
diff --git a/maildir/update.go b/maildir/update.go
index 5fa55af..f30c2dd 100644
--- a/maildir/update.go
+++ b/maildir/update.go
@@ -5,13 +5,13 @@ import (
	"os"
	"path"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/log"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/emersion/go-maildir"
)

// ApplyDiff applies the changes from offmap, updates the internal state
func (s *Store) ApplyDiff(state *offmap.State, c *offmap.Diff) error {
func (s *Store) ApplyDiff(state *state.State, c *state.Diff) error {
	s.state = state
	// Apply changes, updating state as we go
	for _, mbox := range c.Mailboxes.Created {
@@ -76,7 +76,7 @@ func (s *Store) ApplyDiff(state *offmap.State, c *offmap.Diff) error {
}

// createMaildir creates a new maildir with the name of the given mbox
func (s *Store) createMaildir(mbox *offmap.Mailbox) error {
func (s *Store) createMaildir(mbox *state.Mailbox) error {
	// create the maildir
	p := path.Join(s.root, mbox.LocalName)
	err := os.MkdirAll(p, 0o700)
@@ -105,7 +105,7 @@ func (s *Store) renameMaildir(orig string, dest string) error {
}

// createEmail creates an email in the maildir
func (s *Store) createEmail(mbox string, emls []*offmap.Email) error {
func (s *Store) createEmail(mbox string, emls []*state.Email) error {
	dir := maildir.Dir(path.Join(s.root, mbox))
	for _, eml := range emls {
		key, wc, err := dir.Create(eml.Flags)
@@ -130,7 +130,7 @@ func (s *Store) createEmail(mbox string, emls []*offmap.Email) error {
}

// addFlags adds the given flags to the given email
func (s *Store) addFlags(mbox string, emls []*offmap.Email, flag maildir.Flag) error {
func (s *Store) addFlags(mbox string, emls []*state.Email, flag maildir.Flag) error {
	dir := maildir.Dir(path.Join(s.root, mbox))
	// don't update eml.Flags here, want to keep state if adding flags fails
	for _, eml := range emls {
@@ -150,7 +150,7 @@ func (s *Store) addFlags(mbox string, emls []*offmap.Email, flag maildir.Flag) e
}

// removeFlags removes a flag from the given email
func (s *Store) removeFlags(mbox string, emls []*offmap.Email, flag maildir.Flag) error {
func (s *Store) removeFlags(mbox string, emls []*state.Email, flag maildir.Flag) error {
	dir := maildir.Dir(path.Join(s.root, mbox))
	for _, eml := range emls {
		newFlags := []maildir.Flag{}
@@ -172,7 +172,7 @@ func (s *Store) removeFlags(mbox string, emls []*offmap.Email, flag maildir.Flag
	return nil
}

func (s *Store) deleteEmail(mbox string, emls []*offmap.Email) error {
func (s *Store) deleteEmail(mbox string, emls []*state.Email) error {
	dir := maildir.Dir(path.Join(s.root, mbox))
	for _, eml := range emls {
		err := dir.Remove(eml.Key)
diff --git a/maildir/update_test.go b/maildir/update_test.go
index ae0be20..09b1f56 100644
--- a/maildir/update_test.go
+++ b/maildir/update_test.go
@@ -5,8 +5,8 @@ import (
	"path"
	"testing"

	"git.sr.ht/~rockorager/offmap"
	"git.sr.ht/~rockorager/offmap/config"
	"git.sr.ht/~rockorager/offmap/state"
	"github.com/emersion/go-maildir"
	"github.com/stretchr/testify/assert"
)
@@ -21,7 +21,7 @@ func TestCreateMaildir(t *testing.T) {
	mboxes, err := store.readMailboxState()
	assert.NoError(t, err)
	assert.Equal(t, 3, len(mboxes))
	mbox := offmap.NewMailbox(0, "local", "remote")
	mbox := state.NewMailbox(0, "local", "remote")
	err = store.createMaildir(mbox)
	mboxes, err = store.readMailboxState()
	assert.NoError(t, err)
@@ -70,7 +70,7 @@ func TestCreateEmail(t *testing.T) {
	mboxes, err := store.readMailboxState()
	assert.NoError(t, err)
	mailbox := mboxes["Inbox"]
	eml := offmap.NewEmail(mailbox, 0, "key", []maildir.Flag{'S'})
	eml := state.NewEmail(mailbox, 0, "key", []maildir.Flag{'S'})

	tmpEml := path.Join(t.TempDir(), "tmpEmail")
	f, err := os.Create(tmpEml)
@@ -85,7 +85,7 @@ func TestCreateEmail(t *testing.T) {
	assert.NoError(t, err)

	assert.Equal(t, 100, len(mboxes["Inbox"].Emails))
	err = store.createEmail("Inbox", []*offmap.Email{eml})
	err = store.createEmail("Inbox", []*state.Email{eml})
	assert.NoError(t, err)
	mboxes, err = store.readCurrentState()
	assert.NoError(t, err)
@@ -102,13 +102,13 @@ func TestAddFlags(t *testing.T) {
	mboxes, err := store.readCurrentState()
	assert.NoError(t, err)

	var eml *offmap.Email
	var eml *state.Email
	mbox := mboxes["Inbox"]
	for _, v := range mbox.Emails {
		eml = v
		break
	}
	err = store.addFlags("Inbox", []*offmap.Email{eml}, maildir.FlagReplied)
	err = store.addFlags("Inbox", []*state.Email{eml}, maildir.FlagReplied)
	assert.NoError(t, err)

	assert.Contains(t, eml.Flags, maildir.FlagReplied)
@@ -124,13 +124,13 @@ func TestRemoveFlags(t *testing.T) {
	mboxes, err := store.readCurrentState()
	assert.NoError(t, err)

	var eml *offmap.Email
	var eml *state.Email
	mbox := mboxes["Inbox"]
	for _, v := range mbox.Emails {
		eml = v
		break
	}
	err = store.removeFlags("Inbox", []*offmap.Email{eml}, maildir.FlagSeen)
	err = store.removeFlags("Inbox", []*state.Email{eml}, maildir.FlagSeen)
	assert.NoError(t, err)

	assert.NotContains(t, eml.Flags, maildir.FlagSeen)
@@ -148,12 +148,12 @@ func TestDeleteEmails(t *testing.T) {

	mbox := mboxes["Inbox"]

	var eml *offmap.Email
	var eml *state.Email
	for _, v := range mbox.Emails {
		eml = v
		break
	}
	err = store.deleteEmail("Inbox", []*offmap.Email{eml})
	err = store.deleteEmail("Inbox", []*state.Email{eml})
	assert.NoError(t, err)

	mboxes, err = store.readCurrentState()
diff --git a/diff.go b/state/diff.go
similarity index 99%
rename from diff.go
rename to state/diff.go
index 39ffdb3..e789f52 100644
--- a/diff.go
+++ b/state/diff.go
@@ -1,4 +1,4 @@
package offmap
package state

import (
	"git.sr.ht/~rockorager/offmap/log"
diff --git a/diff_test.go b/state/diff_test.go
similarity index 99%
rename from diff_test.go
rename to state/diff_test.go
index d60709f..79cff0e 100644
--- a/diff_test.go
+++ b/state/diff_test.go
@@ -1,4 +1,4 @@
package offmap
package state

import (
	"fmt"
diff --git a/email.go b/state/email.go
similarity index 98%
rename from email.go
rename to state/email.go
index 7d9fdf5..fc4795a 100644
--- a/email.go
+++ b/state/email.go
@@ -1,4 +1,4 @@
package offmap
package state

import (
	"bytes"
diff --git a/mailbox.go b/state/mailbox.go
similarity index 98%
rename from mailbox.go
rename to state/mailbox.go
index b9d1449..1616789 100644
--- a/mailbox.go
+++ b/state/mailbox.go
@@ -1,4 +1,4 @@
package offmap
package state

// Mailbox is the internal representation of a mailbox state
type Mailbox struct {
diff --git a/state.go b/state/state.go
similarity index 97%
rename from state.go
rename to state/state.go
index dbed53a..51f220b 100644
--- a/state.go
+++ b/state/state.go
@@ -1,4 +1,4 @@
package offmap
package state

import (
	"encoding/json"
@@ -26,8 +26,8 @@ type State struct {
	mboxes map[uint32]*Mailbox
}

// NewState creates a new application state and validates the config
func NewState(cfg *config.Config) (*State, error) {
// New creates a new application state and validates the config
func New(cfg *config.Config) (*State, error) {
	root := cfg.Maildir
	if root == "" {
		return nil, fmt.Errorf("offmap: no maildir path set")
diff --git a/state_test.go b/state/state_test.go
similarity index 94%
rename from state_test.go
rename to state/state_test.go
index 6c8734f..5ea7655 100644
--- a/state_test.go
+++ b/state/state_test.go
@@ -1,4 +1,4 @@
package offmap
package state

import (
	"fmt"
@@ -13,7 +13,7 @@ func TestState(t *testing.T) {
	cfg := &config.Config{
		Maildir: tmp,
	}
	state, err := NewState(cfg)
	state, err := New(cfg)
	assert.NoError(t, err)

	err = state.Load()
-- 
2.38.1

[PATCH offmap 3/3] refactor: move main package to project root Export this patch

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 Makefile                   | 2 +-
 cmd/diff.go => diff.go     | 0
 cmd/offmap.go => offmap.go | 0
 cmd/sync.go => sync.go     | 0
 4 files changed, 1 insertion(+), 1 deletion(-)
 rename cmd/diff.go => diff.go (100%)
 rename cmd/offmap.go => offmap.go (100%)
 rename cmd/sync.go => sync.go (100%)

diff --git a/Makefile b/Makefile
index a07d923..d7df416 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ GOSRC+=go.mod go.sum
all: offmap docs

offmap: $(GOSRC) Makefile
	$(GO) build -ldflags "$(GO_LDFLAGS)" -tags "$(GO_TAGS)" $(GO_FLAGS) -o $@ git.sr.ht/~rockorager/offmap/cmd
	$(GO) build -ldflags "$(GO_LDFLAGS)" -tags "$(GO_TAGS)" $(GO_FLAGS) -o $@ git.sr.ht/~rockorager/offmap

.PHONY: docs
docs: $(DOC_TARGETS)
diff --git a/cmd/diff.go b/diff.go
similarity index 100%
rename from cmd/diff.go
rename to diff.go
diff --git a/cmd/offmap.go b/offmap.go
similarity index 100%
rename from cmd/offmap.go
rename to offmap.go
diff --git a/cmd/sync.go b/sync.go
similarity index 100%
rename from cmd/sync.go
rename to sync.go
-- 
2.38.1
offmap/patches/.build.yml: FAILED in 1m49s

[RFC: Package restructuring][0] from [Moritz Poldrack][1]

[0]: https://lists.sr.ht/~rockorager/offmap/patches/37335
[1]: mailto:git@moritz.sh

✗ #897634 FAILED offmap/patches/.build.yml https://builds.sr.ht/~rockorager/job/897634