Skip to content

Commit

Permalink
WIP interface for #112
Browse files Browse the repository at this point in the history
To unblock Umair. Working on implementing this.
  • Loading branch information
lawrence-forooghian committed Nov 14, 2024
1 parent 609ea6d commit f473b57
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 16 deletions.
44 changes: 30 additions & 14 deletions Sources/AblyChat/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public let errorDomain = "AblyChatErrorDomain"
The error codes for errors in the ``errorDomain`` error domain.
*/
public enum ErrorCode: Int {
case nonspecific = 40000

/// ``Rooms.get(roomID:options:)`` was called with a different set of room options than was used on a previous call. You must first release the existing room instance using ``Rooms.release(roomID:)``.
///
/// TODO this code is a guess, revisit in https://github.com/ably-labs/ably-chat-swift/issues/32
Expand All @@ -36,7 +38,8 @@ public enum ErrorCode: Int {
internal var statusCode: Int {
// TODO: These are currently a guess, revisit once outstanding spec question re status codes is answered (https://github.com/ably/specification/pull/200#discussion_r1755222945), and also revisit in https://github.com/ably-labs/ably-chat-swift/issues/32
switch self {
case .inconsistentRoomOptions,
case .nonspecific,
.inconsistentRoomOptions,
.messagesDetachmentFailed,
.presenceDetachmentFailed,
.reactionsDetachmentFailed,
Expand Down Expand Up @@ -69,6 +72,8 @@ internal enum ChatError {
case roomInFailedState
case roomIsReleasing
case roomIsReleased
case presenceOperationRequiresRoomAttach(feature: RoomFeature)
case presenceOperationDisallowedForCurrentRoomStatus(feature: RoomFeature)

/// The ``ARTErrorInfo.code`` that should be returned for this error.
internal var code: ErrorCode {
Expand Down Expand Up @@ -107,20 +112,14 @@ internal enum ChatError {
.roomIsReleasing
case .roomIsReleased:
.roomIsReleased
case .presenceOperationRequiresRoomAttach,
.presenceOperationDisallowedForCurrentRoomStatus:
.nonspecific
}
}

/// A helper type for parameterising the construction of error messages.
private enum AttachOrDetach {
case attach
case detach
}

private static func localizedDescription(
forFailureOfOperation operation: AttachOrDetach,
feature: RoomFeature
) -> String {
let featureDescription = switch feature {
private static func descriptionOfFeature(_ feature: RoomFeature) -> String {
switch feature {
case .messages:
"messages"
case .occupancy:
Expand All @@ -132,15 +131,26 @@ internal enum ChatError {
case .typing:
"typing"
}
}

/// A helper type for parameterising the construction of error messages.
private enum AttachOrDetach {
case attach
case detach
}

private static func localizedDescription(
forFailureOfOperation operation: AttachOrDetach,
feature: RoomFeature
) -> String {
let operationDescription = switch operation {
case .attach:
"attach"
case .detach:
"detach"
}

return "The \(featureDescription) feature failed to \(operationDescription)."
return "The \(descriptionOfFeature(feature)) feature failed to \(operationDescription)."
}

/// The ``ARTErrorInfo.localizedDescription`` that should be returned for this error.
Expand All @@ -158,6 +168,10 @@ internal enum ChatError {
"Cannot perform operation because the room is in a releasing state."
case .roomIsReleased:
"Cannot perform operation because the room is in a released state."
case let .presenceOperationRequiresRoomAttach(feature):
"To perform this \(Self.descriptionOfFeature(feature)) operation, you must first attach the room."
case let .presenceOperationDisallowedForCurrentRoomStatus(feature):
"This \(Self.descriptionOfFeature(feature)) operation can not be performed given the current room status."
}
}

Expand All @@ -171,7 +185,9 @@ internal enum ChatError {
case .inconsistentRoomOptions,
.roomInFailedState,
.roomIsReleasing,
.roomIsReleased:
.roomIsReleased,
.presenceOperationRequiresRoomAttach,
.presenceOperationDisallowedForCurrentRoomStatus:
nil
}
}
Expand Down
21 changes: 20 additions & 1 deletion Sources/AblyChat/RoomFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,26 @@ internal enum RoomFeature {

/// Provides all of the channel-related functionality that a room feature (e.g. an implementation of ``Messages``) needs.
///
/// This mishmash exists to give a room feature access to both:
/// This mishmash exists to give a room feature access to:
///
/// - a `RealtimeChannelProtocol` object (this is the interface that our features are currently written against, as opposed to, say, `RoomLifecycleContributorChannel`)
/// - the discontinuities emitted by the room lifecycle
/// - the presence-readiness wait mechanism supplied by the room lifecycle
internal protocol FeatureChannel: Sendable, EmitsDiscontinuities {
var channel: RealtimeChannelProtocol { get }

/// Waits until we can perform presence operations on the contributors of this room without triggering an implicit attach.
///
/// Implements the checks described by CHA-PR3d, CHA-PR3e, CHA-PR3f, and CHA-PR3g (and similar ones described by other functionality that performs contributor presence operations). Namely:
///
/// - CHA-PR3d: If the room is in the ATTACHING status, it waits for the current ATTACH to complete and then returns. If the current ATTACH fails, then it re-throws that operation’s error.
/// - CHA-PR3e: If the room is in the ATTACHED status, it returns immediately.
/// - CHA-PR3f: If the room is in the DETACHED status, it throws an `ARTErrorInfo` derived from ``ChatError.presenceOperationRequiresRoomAttach(feature:)``.
/// - CHA-PR3g: If the room is in any other status, it throws an `ARTErrorInfo` derived from ``ChatError.presenceOperationDisallowedForCurrentRoomStatus(feature:)``.
///
/// - Parameters:
/// - requester: The room feature that wishes to perform a presence operation. This is only used for customising the message of the thrown error.
func waitToBeAbleToPerformPresenceOperations(requestedByFeature requester: RoomFeature) async throws(ARTErrorInfo)
}

internal struct DefaultFeatureChannel: FeatureChannel {
Expand All @@ -44,4 +58,9 @@ internal struct DefaultFeatureChannel: FeatureChannel {
internal func subscribeToDiscontinuities() async -> Subscription<ARTErrorInfo> {
await contributor.subscribeToDiscontinuities()
}

internal func waitToBeAbleToPerformPresenceOperations(requestedByFeature requester: RoomFeature) async throws(ARTErrorInfo) {
// I’m working on this
fatalError("TODO")
}
}
15 changes: 14 additions & 1 deletion Tests/AblyChatTests/Mocks/MockFeatureChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ final actor MockFeatureChannel: FeatureChannel {
let channel: RealtimeChannelProtocol
// TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36)
private var discontinuitySubscriptions: [Subscription<ARTErrorInfo>] = []
private let resultOfWaitToBeAblePerformPresenceOperations: Result<Void, ARTErrorInfo>?

init(channel: RealtimeChannelProtocol) {
init(
channel: RealtimeChannelProtocol,
resultOfWaitToBeAblePerformPresenceOperations: Result<Void, ARTErrorInfo>? = nil
) {
self.channel = channel
self.resultOfWaitToBeAblePerformPresenceOperations = resultOfWaitToBeAblePerformPresenceOperations
}

func subscribeToDiscontinuities() async -> Subscription<ARTErrorInfo> {
Expand All @@ -21,4 +26,12 @@ final actor MockFeatureChannel: FeatureChannel {
subscription.emit(discontinuity)
}
}

func waitToBeAbleToPerformPresenceOperations(requestedByFeature requester: RoomFeature) async throws(ARTErrorInfo) {
guard let resultOfWaitToBeAblePerformPresenceOperations else {
fatalError("resultOfWaitToBeAblePerformPresenceOperations must be set before waitToBeAbleToPerformPresenceOperations is called")
}

try resultOfWaitToBeAblePerformPresenceOperations.get()
}
}

0 comments on commit f473b57

Please sign in to comment.