package preset import ( "encoding/json" "fmt" "strings" ) // ImportMode is what to do with the presets already on disk. type ImportMode string const ( // ImportMerge keeps existing presets and adds the incoming ones. ImportMerge ImportMode = "merge" // ImportReplace discards everything already saved. ImportReplace ImportMode = "replace" ) // Export renders the presets as a shippable preset pack. Passing one or more // group names filters to just those groups; passing none exports everything. // // The output is the same shape as presets.json so a pack can be dropped // straight in as a starting file, and it stays hand-editable. func (s *Store) Export(groups ...string) ([]byte, error) { want := make(map[string]bool, len(groups)) for _, g := range groups { want[strings.ToLower(strings.TrimSpace(g))] = true } out := File{Version: SchemaVersion} for _, p := range s.All() { if len(want) > 0 && !want[strings.ToLower(p.GroupOrUngrouped())] { continue } out.Presets = append(out.Presets, p) } b, err := json.MarshalIndent(out, "", " ") if err != nil { return nil, fmt.Errorf("encoding export: %w", err) } return append(b, '\n'), nil } // ImportResult reports what an import did, for the summary shown afterwards. type ImportResult struct { Added int `json:"added"` Replaced int `json:"replaced"` Renamed []string `json:"renamed,omitempty"` } // Import validates an incoming preset file in full before touching anything, // then applies it. A file that fails validation changes nothing on disk. // // On merge, an incoming preset whose id already exists is kept alongside the // existing one: it gets a fresh id and "(imported)" appended to its name, so a // colleague's pack can never silently overwrite what you already had. func (s *Store) Import(data []byte, mode ImportMode) (ImportResult, error) { incoming, err := Decode(data) if err != nil { return ImportResult{}, err } var res ImportResult switch mode { case ImportReplace: s.mu.Lock() s.file = File{Version: SchemaVersion, Presets: incoming.Presets} res.Added = len(incoming.Presets) s.mu.Unlock() case ImportMerge: s.mu.Lock() existing := make(map[string]bool, len(s.file.Presets)) for _, p := range s.file.Presets { existing[p.ID] = true } for _, p := range incoming.Presets { if existing[p.ID] { p.ID = NewID() p.Name = p.Name + " (imported)" res.Renamed = append(res.Renamed, p.Name) } existing[p.ID] = true s.file.Presets = append(s.file.Presets, p) res.Added++ } s.mu.Unlock() default: return ImportResult{}, fmt.Errorf("unknown import mode %q", mode) } if err := s.Save(); err != nil { // Reload so the in-memory copy matches whatever is actually on disk. _ = s.Load() return ImportResult{}, err } return res, nil }