package middleware import ( "fmt" "strings" "sync" "git.sr.ht/~rjarry/aerc/worker/types" ) type folderMapper struct { sync.Mutex types.WorkerInteractor fm folderMap table map[string]string } func NewFolderMapper(base types.WorkerInteractor, mapping map[string]string, order []string, ) types.WorkerInteractor { base.Infof("loading worker middleware: foldermapper") return &folderMapper{ WorkerInteractor: base, fm: folderMap{mapping, order}, table: make(map[string]string), } } func (f *folderMapper) incoming(msg types.WorkerMessage, dir string) string { f.Lock() defer f.Unlock() mapped, ok := f.table[dir] if !ok { return dir } return mapped } func (f *folderMapper) outgoing(msg types.WorkerMessage, dir string) string { f.Lock() defer f.Unlock() for k, v := range f.table { if v == dir { mapped := k return mapped } } return dir } func (f *folderMapper) store(s string) { f.Lock() defer f.Unlock() display := f.fm.Apply(s) f.table[display] = s f.Tracef("store display folder '%s' to '%s'", display, s) } func (f *folderMapper) create(s string) (string, error) { f.Lock() defer f.Unlock() backend := createFolder(f.table, s) if _, exists := f.table[s]; exists { return s, fmt.Errorf("folder already exists: %s", s) } f.table[s] = backend f.Tracef("create display folder '%s' as '%s'", s, backend) return backend, nil } func (f *folderMapper) ProcessAction(msg types.WorkerMessage) types.WorkerMessage { switch msg := msg.(type) { case *types.CheckMail: for i := range msg.Directories { msg.Directories[i] = f.incoming(msg, msg.Directories[i]) } case *types.CopyMessages: msg.Destination = f.incoming(msg, msg.Destination) case *types.AppendMessage: msg.Destination = f.incoming(msg, msg.Destination) case *types.MoveMessages: msg.Destination = f.incoming(msg, msg.Destination) case *types.CreateDirectory: var err error msg.Directory, err = f.create(msg.Directory) if err != nil { f.Errorf("error creating new directory: %v", err) } case *types.RemoveDirectory: msg.Directory = f.incoming(msg, msg.Directory) case *types.OpenDirectory: msg.Directory = f.incoming(msg, msg.Directory) } return f.WorkerInteractor.ProcessAction(msg) } func (f *folderMapper) PostMessage(msg types.WorkerMessage, cb func(m types.WorkerMessage)) { switch msg := msg.(type) { case *types.Done: switch msg := msg.InResponseTo().(type) { case *types.CheckMail: for i := range msg.Directories { msg.Directories[i] = f.outgoing(msg, msg.Directories[i]) } case *types.CopyMessages: msg.Destination = f.outgoing(msg, msg.Destination) case *types.AppendMessage: msg.Destination = f.outgoing(msg, msg.Destination) case *types.MoveMessages: msg.Destination = f.outgoing(msg, msg.Destination) case *types.CreateDirectory: msg.Directory = f.outgoing(msg, msg.Directory) case *types.RemoveDirectory: msg.Directory = f.outgoing(msg, msg.Directory) case *types.OpenDirectory: msg.Directory = f.outgoing(msg, msg.Directory) } case *types.CheckMailDirectories: for i := range msg.Directories { msg.Directories[i] = f.outgoing(msg, msg.Directories[i]) } case *types.Directory: f.store(msg.Dir.Name) msg.Dir.Name = f.outgoing(msg, msg.Dir.Name) case *types.DirectoryInfo: msg.Info.Name = f.outgoing(msg, msg.Info.Name) } f.WorkerInteractor.PostMessage(msg, cb) } // folderMap contains the mapping between the ui and backend folder names type folderMap struct { mapping map[string]string order []string } // Apply applies the mapping from the folder map to the backend folder func (f *folderMap) Apply(s string) string { for _, k := range f.order { v := f.mapping[k] strict := true if strings.HasSuffix(v, "*") { v = strings.TrimSuffix(v, "*") strict = false } if (strings.HasPrefix(s, v) && !strict) || (s == v && strict) { term := strings.TrimPrefix(s, v) if strings.Contains(k, "*") && !strict { prefix := k for strings.Contains(prefix, "**") { prefix = strings.ReplaceAll(prefix, "**", "*") } s = strings.Replace(prefix, "*", term, 1) } else { s = k + term } } } return s } // createFolder reverses the mapping of a new folder name func createFolder(table map[string]string, s string) string { max, key := 0, "" for k := range table { if strings.HasPrefix(s, k) && len(k) > max { max, key = len(k), k } } if max > 0 && key != "" { s = table[key] + strings.TrimPrefix(s, key) } return s }