Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give Headers an API that’s consistent with JSONValue #205

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 63 additions & 6 deletions Sources/AblyChat/Headers.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,71 @@
// TODO: https://github.com/ably-labs/ably-chat-swift/issues/13 - try to improve this type

/// A value that can be used in ``Headers``. It is the same as ``JSONValue`` except it does not have the `object` or `array` cases.
public enum HeadersValue: Sendable, Equatable {
case string(String)
case number(Double)
case bool(Bool)
case null

// MARK: - Convenience getters for associated values

/// If this `HeadersValue` has case `string`, this returns the associated value. Else, it returns `nil`.
public var stringValue: String? {
if case let .string(stringValue) = self {
stringValue
} else {
nil
}
}

/// If this `HeadersValue` has case `number`, this returns the associated value. Else, it returns `nil`.
public var numberValue: Double? {
if case let .number(numberValue) = self {
numberValue
} else {
nil
}
}

/// If this `HeadersValue` has case `bool`, this returns the associated value. Else, it returns `nil`.
public var boolValue: Bool? {
if case let .bool(boolValue) = self {
boolValue
} else {
nil
}
}

/// Returns true if and only if this `HeadersValue` has case `null`.
public var isNull: Bool {
if case .null = self {
true
} else {
false
}
}
}

extension HeadersValue: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}

extension HeadersValue: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .number(Double(value))
}
}

extension HeadersValue: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .number(value)
}
}

extension HeadersValue: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}

extension HeadersValue: JSONDecodable {
Expand Down Expand Up @@ -43,10 +104,6 @@ extension HeadersValue: JSONEncodable {
}
}

// The corresponding type in TypeScript is
// Record<string, number | string | boolean | null | undefined>
// There may be a better way to represent it in Swift; this will do for now. Have omitted `undefined` because I don’t know how that would occur.

/**
* Headers are a flat key-value map that can be attached to chat messages.
*
Expand Down
2 changes: 1 addition & 1 deletion Tests/AblyChatTests/ChatAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ struct ChatAPITests {
params: .init(
text: "", // arbitrary
// The exact value here is arbitrary, just want to check it gets serialized
headers: ["numberKey": .number(10), "stringKey": .string("hello")]
headers: ["numberKey": 10, "stringKey": "hello"]
)
)

Expand Down
2 changes: 1 addition & 1 deletion Tests/AblyChatTests/DefaultRoomReactionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct DefaultRoomReactionsTests {
let sendReactionParams = SendReactionParams(
type: "like",
metadata: ["someMetadataKey": "someMetadataValue"],
headers: ["someHeadersKey": HeadersValue.string("someHeadersValue")]
headers: ["someHeadersKey": "someHeadersValue"]
)

// When
Expand Down
4 changes: 2 additions & 2 deletions Tests/AblyChatTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct IntegrationTests {
params: .init(
text: "Hello from txRoom, after rxRoom subscribe",
metadata: ["someMetadataKey": 123, "someOtherMetadataKey": "foo"],
headers: ["someHeadersKey": .number(456), "someOtherHeadersKey": .string("bar")]
headers: ["someHeadersKey": 456, "someOtherHeadersKey": "bar"]
)
)
let rxMessageFromSubscription = try #require(await rxMessageSubscription.first { _ in true })
Expand Down Expand Up @@ -169,7 +169,7 @@ struct IntegrationTests {
params: .init(
type: "heart",
metadata: ["someMetadataKey": 123, "someOtherMetadataKey": "foo"],
headers: ["someHeadersKey": .number(456), "someOtherHeadersKey": .string("bar")]
headers: ["someHeadersKey": 456, "someOtherHeadersKey": "bar"]
)
)
let rxReactionFromSubscription = try #require(await rxReactionSubscription.first { _ in true })
Expand Down
4 changes: 2 additions & 2 deletions Tests/AblyChatTests/RoomReactionDTOTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ enum RoomReactionDTOTests {
]
)

#expect(data == .init(headers: ["someStringKey": .string("someStringValue"), "someNumberKey": .number(123)]))
#expect(data == .init(headers: ["someStringKey": "someStringValue", "someNumberKey": 123]))
}

// MARK: - JSONCodable
Expand All @@ -100,7 +100,7 @@ enum RoomReactionDTOTests {

@Test
func toJSONValue() {
let data = RoomReactionDTO.Extras(headers: ["someStringKey": .string("someStringValue"), "someNumberKey": .number(123)])
let data = RoomReactionDTO.Extras(headers: ["someStringKey": "someStringValue", "someNumberKey": 123])

#expect(data.toJSONValue == [
"headers": [
Expand Down
Loading