contacts.go

 1package macos
 2
 3import (
 4	_ "embed"
 5	"encoding/json"
 6	"fmt"
 7	"os"
 8	"os/exec"
 9	"path/filepath"
10	"runtime"
11)
12
13//go:embed contacts.swift
14var contactsSwift string
15
16type MacOSContact struct {
17	Name   string   `json:"name"`
18	Emails []string `json:"emails"`
19}
20
21// FetchContacts calls the macOS Contacts framework via a compiled Swift helper.
22func FetchContacts() ([]MacOSContact, error) {
23	if runtime.GOOS != "darwin" {
24		return nil, fmt.Errorf("FetchContacts is only supported on macOS")
25	}
26
27	tmpDir, err := os.MkdirTemp("", "matcha-macos")
28	if err != nil {
29		return nil, err
30	}
31	defer os.RemoveAll(tmpDir) //nolint:errcheck
32
33	swiftFile := filepath.Join(tmpDir, "contacts.swift")
34	if err := os.WriteFile(swiftFile, []byte(contactsSwift), 0644); err != nil {
35		return nil, err
36	}
37
38	binFile := filepath.Join(tmpDir, "contacts")
39
40	// Compile the Swift helper
41	cmd := exec.Command("swiftc", swiftFile, "-o", binFile) //nolint:noctx
42	if out, err := cmd.CombinedOutput(); err != nil {
43		return nil, fmt.Errorf("failed to compile contacts helper: %w\n%s", err, string(out))
44	}
45
46	// Run the helper
47	out, err := exec.Command(binFile).Output() //nolint:noctx
48	if err != nil {
49		return nil, fmt.Errorf("failed to run contacts helper: %w", err)
50	}
51
52	var contacts []MacOSContact
53	if err := json.Unmarshal(out, &contacts); err != nil {
54		return nil, fmt.Errorf("failed to parse contacts JSON: %w\nOutput: %s", err, string(out))
55	}
56
57	return contacts, nil
58}