|
| 1 | +package xpc |
| 2 | + |
| 3 | +/* |
| 4 | +#cgo darwin CFLAGS: -mmacosx-version-min=11 -x objective-c -fno-objc-arc |
| 5 | +# include "xpc.h" |
| 6 | +*/ |
| 7 | +import "C" |
| 8 | +import ( |
| 9 | + "fmt" |
| 10 | + "iter" |
| 11 | + "runtime/cgo" |
| 12 | + "unsafe" |
| 13 | +) |
| 14 | + |
| 15 | +// Dictionary represents an XPC dictionary (XPC_TYPE_DICTIONARY) object. [TypeDictionary] |
| 16 | +// - https://developer.apple.com/documentation/xpc/xpc_type_dictionary-c.macro?language=objc |
| 17 | +type Dictionary struct { |
| 18 | + *XpcObject |
| 19 | +} |
| 20 | + |
| 21 | +var _ Object = &Dictionary{} |
| 22 | + |
| 23 | +// NewDictionary creates a new empty [Dictionary] object and applies the given entries. |
| 24 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_create_empty()?language=objc |
| 25 | +// |
| 26 | +// The entries can be created using [DictionaryEntry] functions such as [KeyValue]. |
| 27 | +func NewDictionary(entries ...DictionaryEntry) *Dictionary { |
| 28 | + d := &Dictionary{XpcObject: &XpcObject{C.xpcDictionaryCreateEmpty()}} |
| 29 | + for _, e := range entries { |
| 30 | + e(d) |
| 31 | + } |
| 32 | + return d |
| 33 | +} |
| 34 | + |
| 35 | +// DictionaryEntry defines a function type for customizing [NewDictionary] or [Dictionary.CreateReply]. |
| 36 | +type DictionaryEntry func(*Dictionary) |
| 37 | + |
| 38 | +// KeyValue sets a [Object] value for the given key in the [Dictionary]. |
| 39 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_set_value(_:_:_:)?language=objc |
| 40 | +func KeyValue(key string, val Object) DictionaryEntry { |
| 41 | + return func(o *Dictionary) { |
| 42 | + o.SetValue(key, val) |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +// DictionaryApplier is a function type for applying to each key-value pair in the XPC dictionary. |
| 47 | +type DictionaryApplier func(string, Object) bool |
| 48 | + |
| 49 | +// callDictionaryApplier is called from C to apply a function to each key-value pair in the XPC dictionary object. |
| 50 | +// |
| 51 | +//export callDictionaryApplier |
| 52 | +func callDictionaryApplier(cgoApplier uintptr, cKey *C.char, cgoValue uintptr) C.bool { |
| 53 | + applier := unwrapHandler[DictionaryApplier](cgoApplier) |
| 54 | + return C.bool(applier(C.GoString(cKey), unwrapObject[Object](cgoValue))) |
| 55 | +} |
| 56 | + |
| 57 | +// All iterates over all key-value pairs in the [Dictionary]. |
| 58 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_apply(_:_:)?language=objc |
| 59 | +func (o *Dictionary) All() iter.Seq2[string, Object] { |
| 60 | + return func(yieald func(string, Object) bool) { |
| 61 | + cgoApplier := cgo.NewHandle(DictionaryApplier(yieald)) |
| 62 | + defer cgoApplier.Delete() |
| 63 | + C.xpcDictionaryApply(o.Raw(), C.uintptr_t(cgoApplier)) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// Keys iterates over all keys in the [Dictionary] using [Dictionary.All]. |
| 68 | +func (o *Dictionary) Keys() iter.Seq[string] { |
| 69 | + return func(yieald func(string) bool) { |
| 70 | + for key := range o.All() { |
| 71 | + if !yieald(key) { |
| 72 | + return |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +// Values iterates over all [Object] values in the [Dictionary] using [Dictionary.All]. |
| 79 | +func (o *Dictionary) Values() iter.Seq[Object] { |
| 80 | + return func(yieald func(Object) bool) { |
| 81 | + for _, value := range o.All() { |
| 82 | + if !yieald(value) { |
| 83 | + return |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +// Entries iterates over all [DictionaryEntry] entries in the [Dictionary] using [Dictionary.All]. |
| 90 | +func (o *Dictionary) Entries() iter.Seq[DictionaryEntry] { |
| 91 | + return func(yieald func(DictionaryEntry) bool) { |
| 92 | + for key, value := range o.All() { |
| 93 | + if !yieald(KeyValue(key, value)) { |
| 94 | + return |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +// GetData retrieves a byte slice value from the [Dictionary] by key. |
| 101 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_data(_:_:_:)?language=objc |
| 102 | +// |
| 103 | +// Returns nil if the key does not exist. |
| 104 | +func (o *Dictionary) GetData(key string) []byte { |
| 105 | + cKey := C.CString(key) |
| 106 | + defer C.free(unsafe.Pointer(cKey)) |
| 107 | + var n C.size_t |
| 108 | + p := C.xpcDictionaryGetData(o.Raw(), cKey, &n) |
| 109 | + if p == nil || n == 0 { |
| 110 | + return nil |
| 111 | + } |
| 112 | + return C.GoBytes(p, C.int(n)) |
| 113 | +} |
| 114 | + |
| 115 | +// GetString retrieves a string value from the [Dictionary] by key. |
| 116 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_string(_:_:)?language=objc |
| 117 | +// |
| 118 | +// Returns an empty string if the key does not exist. |
| 119 | +func (o *Dictionary) GetString(key string) string { |
| 120 | + cKey := C.CString(key) |
| 121 | + defer C.free(unsafe.Pointer(cKey)) |
| 122 | + val := C.xpcDictionaryGetString(o.Raw(), cKey) |
| 123 | + return C.GoString(val) |
| 124 | +} |
| 125 | + |
| 126 | +// SetValue sets an [Object] value for the given key in the [Dictionary]. |
| 127 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_set_value(_:_:_:)?language=objc |
| 128 | +func (o *Dictionary) SetValue(key string, val Object) { |
| 129 | + cKey := C.CString(key) |
| 130 | + defer C.free(unsafe.Pointer(cKey)) |
| 131 | + C.xpcDictionarySetValue(o.Raw(), cKey, val.Raw()) |
| 132 | +} |
| 133 | + |
| 134 | +// GetValue retrieves an [Object] value from the [Dictionary] by key. |
| 135 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_get_value(_:_:)?language=objc |
| 136 | +// |
| 137 | +// Returns nil if the key does not exist. |
| 138 | +func (o *Dictionary) GetValue(key string) Object { |
| 139 | + cKey := C.CString(key) |
| 140 | + defer C.free(unsafe.Pointer(cKey)) |
| 141 | + val := C.xpcDictionaryGetValue(o.Raw(), cKey) |
| 142 | + if val == nil { |
| 143 | + return nil |
| 144 | + } |
| 145 | + return NewObject(val) |
| 146 | +} |
| 147 | + |
| 148 | +// DictionaryCreateReply creates a new reply [Dictionary] based on the current [Dictionary]. |
| 149 | +// - https://developer.apple.com/documentation/xpc/xpc_dictionary_create_reply(_:)?language=objc |
| 150 | +// |
| 151 | +// The entries can be created using [DictionaryEntry] functions such as [KeyValue]. |
| 152 | +func (o *Dictionary) CreateReply(entries ...DictionaryEntry) *Dictionary { |
| 153 | + d := &Dictionary{XpcObject: &XpcObject{C.xpcDictionaryCreateReply(o.Raw())}} |
| 154 | + for _, entry := range entries { |
| 155 | + entry(d) |
| 156 | + } |
| 157 | + return d |
| 158 | +} |
| 159 | + |
| 160 | +// SenderSatisfies checks if the sender of the message [Dictionary] satisfies the given [PeerRequirement]. |
| 161 | +// - https://developer.apple.com/documentation/xpc/xpc_peer_requirement_match_received_message?language=objc |
| 162 | +func (d *Dictionary) SenderSatisfies(requirement *PeerRequirement) (bool, error) { |
| 163 | + var err_out unsafe.Pointer |
| 164 | + res := C.xpcPeerRequirementMatchReceivedMessage(requirement.Raw(), d.Raw(), &err_out) |
| 165 | + if err_out != nil { |
| 166 | + return false, fmt.Errorf("error matching peer requirement: %w", newRichError(err_out)) |
| 167 | + } |
| 168 | + return bool(res), nil |
| 169 | +} |
0 commit comments