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}