Repository navigation
Expose the raw AppleEvent descriptor through a .wildCard return type - #8
Merged
Merged
Conversation
Some application properties answer with a different type depending on state: the value of a
cell in Numbers is a number, a date, text, a boolean, or missing value when the cell is empty.
Every return type coerced that distinction away before Swift saw it, since the caller had to
declare one type up front and NSAppleScript's descriptor was discarded once it had.
The new .wildCard return type switches on the descriptor's own type instead, and returns an
AppleScriptValue carrying what it found: numbers, text, booleans, dates, files, missing value,
application-defined constants, and lists and records, the last two decoded recursively. A type
the enum does not decode arrives as .unknown, carrying the descriptor untouched, so nothing is
lost and the caller can drill in. Integers are read at the width their descriptor declares,
since int32Value answers zero for anything wider.
Record keys need unpacking: an Apple event record is keyed by four-character code, so the keys
a caller writes are packed into a keyASUserRecordFields list of alternating names and values.
Keys AppleScript owns as terminology are compiled to codes before the script runs and come
through as those, which is why {name:"Roger"} decodes as ["pnam": .string("Roger")].
Constants carry their raw code, as there is no general mapping from a code to a meaning: what
'autp' means is defined by the target application's dictionary. String's appleScriptFourCharCode
conversions make one comparable without hand-assembling the bytes.
osascript prints its result as text, so the descriptor is gone before the command-line method
could read it. That method throws the new .unsupportedReturnType error before launching the
process, so a script declaring .wildCard never runs and never touches the target application.
Closes #5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two layers, both permission-free. The first builds descriptors directly and covers what cannot
be produced from AppleScript source: a 64-bit integer, whose int32Value is asserted to fail so
the reason for reading the bytes is recorded; an unsigned value too large for an Int, which has
to fall back to the descriptor rather than report a wrong number; an empty list, whose index
range would trap if built naively; and a record carrying both a coded key and user fields,
including the collision where a user field is named after a code.
The second runs self-contained scripts through NSAppleScript. They drive no application, so
they need no Automation permission, and they are the only way to check the decoding against
what AppleScript actually emits — that {name:"Roger", age:42} really does arrive with name
compiled to 'pnam', and that 3000000000 really does arrive as a real rather than an integer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README gets the Numbers cell example the request came from, a table of what each AppleScriptValue case carries, and the three things that surprise: constants arrive as raw codes, record keys arrive as written only when AppleScript did not claim them as terminology, and the command-line method refuses the type outright. API.md gets a section of its own, mapping each case to the descriptor types it decodes. The example file gets a Finder script, so it stays runnable on any Mac without extra software: a disk's format is an edfm enumeration, and its constants are compared at the call site against the codes Finder's dictionary documents, which is what working with .constant looks like. Every snippet in both documents was compiled against the package before being written down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A minor bump rather than a patch: the release adds public API. Consumers switching exhaustively over AppleScriptError or AppleScriptReturnType have a new case to handle, which the release notes call out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The caveat was the last bullet of the release's notes and named only AppleScriptError. It now opens the release, where an upgrader reads it before anything else, and names both enums that gained a case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Looks great! You even included UserRecordFields coercion! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5.
What this adds
A
.wildCardreturn type, for the properties that answer with a different type depending on state. Thevalueof a cell in Numbers is documented as "number, date, text, boolean, or missing value" — declare.intthere and an empty cell is indistinguishable from a cell holding zero.NSAppleScriptalready hands back anNSAppleEventDescriptorcarrying the real type; the bridge discarded it and coerced to whatever the caller declared..wildCardswitches on the descriptor's own type instead and returns anAppleScriptValue:Numbers, text, booleans, dates, files,
missing value, application-defined constants, and lists and records, the last two decoded recursively. Anything else arrives as.unknowncarrying the descriptor untouched, so nothing is lost.Decisions worth reviewing
.wildCard, not.descriptor. @olofhellman's suggestion in Expose the raw AppleEvent descriptor, so callers can switch on the actual result type #5, after the AppleEventstypeWildCardtradition. The escape hatch inside the value enum stays.unknown, so the two names don't collide.missing valuegets its own case, rather than arriving as.constant('msng'). It is the common answer, and making every caller compare four-character codes for it would be the wrong default. Other constants do carry their rawFourCharCode— what'autp'means is defined by the application's dictionary, not by AppleScript, so there is no general mapping to hand the caller.osascriptparity.executeAppleScriptViaCommandLinethrows the newAppleScriptError.unsupportedReturnTypebefore launching the process, so a.wildCardscript never runs and never touches the target application. Returning the printed text as a.stringwould fake the very type information the feature exists to preserve.{name:"Roger", age:42}decodes as["pnam": .string("Roger"), "age": .int(42)]. Documented everywhere it can surprise.int32Valueanswers0for anything wider, which would have turned atypeSInt64into a silent zero. A value too large for anIntfalls back to.unknownrather than reporting a wrong number.Testing
46 tests, all passing locally. 17 are new, in two layers — both permission-free:
Int, an empty list, and a record carrying both a coded key and user fields, including the collision where a user field is named after a code.NSAppleScript, which drive no application and so need no Automation permission. These are the only check against what AppleScript actually emits — that{name:"Roger", age:42}really does arrive withnamecompiled to'pnam', and that3000000000really does arrive as a real.Every code snippet in the README and in
Documentation/API.mdwas compiled against the package before being written down, andExamples/AppleScripts.swiftbuilds as a consumer package the way CI builds it.Version
Cut as 1.2.0: new public API, all of it opt-in, with the default path unchanged.
AppleScriptReturnTypeandAppleScriptErroreach gained a case, so code switching over either exhaustively without adefaultstops compiling — the release notes open with that caveat.Two things I could not verify
NSAppleScriptexecution tests are unproven on a GitHub runner. They pass locally; if the runner disagrees, they are easy to drop without losing the descriptor-level coverage.Follow-up
#7 tracks @olofhellman's second suggestion, an
.osTypereturn type for scripts whose result is known to be a constant. Left out deliberately: it carries its own unsettled question — raw codes versus dictionary terms — that deserves its own discussion.🤖 Generated with Claude Code