package autofillcache import ( "encoding/json" "os" "path/filepath" "slices" "strings" "time" ) type BindingsFile struct { UpdatedAt string `json:"updatedAt"` Apps map[string]string `json:"apps,omitempty"` } func ReadBindings(path string) (BindingsFile, error) { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return BindingsFile{}, nil } return BindingsFile{}, err } var bindings BindingsFile if err := json.Unmarshal(data, &bindings); err != nil { return BindingsFile{}, err } if bindings.Apps == nil { bindings.Apps = make(map[string]string) } return bindings, nil } func RememberBinding(path, rawTarget, entryID string, now time.Time) error { bindings, err := ReadBindings(path) if err != nil { return err } if bindings.Apps == nil { bindings.Apps = make(map[string]string) } target := strings.TrimSpace(rawTarget) id := strings.TrimSpace(entryID) if target == "" || id == "" { return nil } bindings.Apps[target] = id bindings.UpdatedAt = now.UTC().Format(time.RFC3339) data, err := json.MarshalIndent(bindings, "", " ") if err != nil { return err } if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err } return os.WriteFile(path, data, 0o600) } func ResolveWithBindings(cache File, bindings BindingsFile, rawTarget string) MatchResult { target := strings.TrimSpace(rawTarget) if entryID := strings.TrimSpace(bindings.Apps[target]); entryID != "" { for _, entry := range cache.Entries { if entry.ID == entryID { return MatchResult{Status: MatchStatusFound, Entry: entry} } } } return Resolve(cache, rawTarget) } func ChooserCandidates(cache File, rawTarget string) []Entry { if result := Resolve(cache, rawTarget); result.Status == MatchStatusFound { return []Entry{result.Entry} } candidates := append([]Entry(nil), cache.Entries...) slices.SortFunc(candidates, func(left, right Entry) int { if cmp := strings.Compare(strings.ToLower(strings.TrimSpace(left.Title)), strings.ToLower(strings.TrimSpace(right.Title))); cmp != 0 { return cmp } if cmp := strings.Compare(strings.ToLower(strings.Join(left.Path, "/")), strings.ToLower(strings.Join(right.Path, "/"))); cmp != 0 { return cmp } return strings.Compare(left.ID, right.ID) }) return candidates }