Skip to content

Releases: WendellXY/Description

0.3.0

Choose a tag to compare

@WendellXY WendellXY released this 25 Sep 05:35

Custom properties move to a new @DescribableProperties macro. This fixes a failure that 0.2.0 introduced on types whose Equatable conformance can't be synthesized. This release also removes optional-interpolation warnings from member-path placeholders.

.package(url: "https://github.com/WendellXY/Description.git", from: "0.3.0")

Fixed

  • Equatable failure with @Describable (0.2.0 regression). On a type whose Equatable conformance can't be synthesized, such as an enum with a closure payload and a hand-written ==, adding @Describable failed with "type does not conform to protocol 'Equatable'" and the same == listed twice. The cause is a compiler bug triggered by macros that declare arbitrary member names, which 0.2.0 added so it could generate custom properties. @Describable now declares only its fixed names, and the case from the report is kept as a regression test.
  • Optional-interpolation warnings from member paths. Placeholders such as {locator.storeType}, and raw expressions such as {_0.productType}, no longer produce "string interpolation produces a debug description for an optional value" when the property at the end of the path is optional. The printed text is unchanged.

Breaking change

Custom properties (@Description("name", …) or .property("name")) now need @DescribableProperties next to @Describable:

@Describable
@DescribableProperties
enum Screen: AnalyticsNaming {
    @Description("analyticsName", "chat_room")
    case chat(roomId: Int)
}

Without it, the compiler reports "custom description property 'analyticsName' requires @DescribableProperties", with a fix-it that adds the macro. Types without custom properties need no changes.

Requirements: Swift 6.0 or later, and swift-syntax 600 through 604. Tested on macOS and on Linux with Swift 6.0, 6.1, and 6.2.

0.2.0

Choose a tag to compare

@WendellXY WendellXY released this 25 Sep 05:08

@Describable now only turns synthesis on. All text comes from @Description, which can set the text for description, errorDescription, debugDescription, or a property of your own, and raw templates accept any Swift expression.

.package(url: "https://github.com/WendellXY/Description.git", from: "0.2.0")
@Describable
@Description("User(id: {id}, name: {name})")
@Description(.debug, "User(id: {id}, name: {name}, tags: {tags.count})")
struct User {
    let id: Int
    let name: String
    let tags: [String]
}

@Describable
enum Route: Error {
    @Description("levelUpgrade(uid: {notify?.uid ?? 0}, hasReward: {notify?.reward?})")
    case levelUpgrade(notify: Notify?)

    @Description(raw: #"cart(active: {items.filter { $0.isActive }.count})"#)
    @Description(.error, raw: #"{String(localized: "cart_failed", bundle: .module)}"#)
    case cart(items: [Item])
}

New

  • Placeholder paths: {info.redPacketId}, optional chaining ({notify?.uid}), literal defaults ({notify?.uid ?? 0}, {id ?? "nil"}), and presence checks ({data?}, which renders true/false). The macro still validates each placeholder's first name, including spelling fix-its and actor isolation.
  • Raw templates: @Description(raw: #"…"#) accepts any Swift expression in {…}, with the type's members and the case's associated values in scope (unlabeled values are _0, _1, …). Each expression must parse.
  • Targets: @Description(.error, …), @Description(.debug, …), and @Description("name", …) / .property("name") set the text of one generated property. .debug adds CustomDebugStringConvertible. A custom name generates var name: String, for example to satisfy a protocol you declare on the type. Targets without their own text use the main text.
  • @Describable(generating:) chooses which targets to generate, e.g. [.debug] for a type that should only be CustomDebugStringConvertible.
  • @Describable(default:) sets the text of cases without a @Description: .caseName (the default), .rawValue, or .member("title").
  • Enum-level text: a @Description on an enum declaration applies to every case that has none of its own.
  • Diagnostics: the macro now also reports duplicate templates for one target, invalid targets, .error on non-error types (with a fix-it that adds Error), invalid raw expressions, and existing CustomDebugStringConvertible conformances or hand-written custom properties. The missing-template fix-it now inserts a memberwise @Description.

Breaking changes

0.1 0.2
@Describable("User({id})") @Describable @Description("User({id})")
@Describable("…", error: "…") @Describable @Description("…") @Description(.error, "…")
@Description("…", error: "…") on a case @Description("…") @Description(.error, "…")
@Description(error: "…") on a case @Description(.error, "…")

The generated text is unchanged for code that is migrated this way.

Requirements: Swift 6.0 or later, and swift-syntax 600 through 604. Tested on macOS and on Linux with Swift 6.0, 6.1, and 6.2.

0.1.1

Choose a tag to compare

@WendellXY WendellXY released this 25 Sep 03:56

Compile-time-validated descriptions for Swift enums, structs, classes, and actors.

.package(url: "https://github.com/WendellXY/Description.git", from: "0.1.1")
@Describable
enum RequestState {
    case idle

    @Description("Loading {url}")
    case loading(url: URL)
}

@Describable("HTTPError(code: {code})", error: "The request failed with HTTP {code}.")
struct HTTPError: Error {
    let code: Int
}

Features

  • @Describable synthesizes CustomStringConvertible for enums, structs, classes, and actors. It also synthesizes LocalizedError when the type lists Error in its inheritance clause.
  • @Description configures individual enum cases. Cases without one are described by their name.
  • Templates support {name}, {0}, and {{ / }}. Placeholders are validated during macro expansion.
  • Diagnostics point inside the template. Fix-its correct misspelled fields, escape stray braces, insert a memberwise template, and add Error conformance.
  • Actor descriptions are nonisolated, and actor-isolated state is rejected at compile time.
  • Existing description, errorDescription, and conformances are reported instead of overridden.

Changes since 0.1.0

  • Added the MIT license.
  • Added GitHub Actions CI, which runs the test suite on macOS and on Linux with Swift 6.0, 6.1, and 6.2.
  • Made the test suite compatible with Swift 6.0.
  • Documented a Foundation crash on Linux before Swift 6.2: localizedDescription crashes on any class-based Error, with or without @Describable. Read errorDescription directly, or use a struct or enum error.

No changes to the macros or their generated code.

Requirements: Swift 6.0 or later, and swift-syntax 600 through 604.